comparison 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
comparison
equal deleted inserted replaced
312:1950bb04a69b 313:b3d46806787d
1 /*
2 * SIDLib common utility functions
3 * Programmed and designed by Matti 'ccr' Hämäläinen <ccr@tnsp.org>
4 * (C) Copyright 2014-2020 Tecnic Software productions (TNSP)
5 */
6 #include "sidutil.h"
7 #include "th_file.h"
8 #include "th_string.h"
9 #include "th_datastruct.h"
10
11
12 void sidutil_print_license(void)
13 {
14 printf("%s - %s\n%s\n", th_prog_name, th_prog_desc, th_prog_author);
15 printf(
16 "\n"
17 "Redistribution and use in source and binary forms, with or without\n"
18 "modification, are permitted provided that the following conditions\n"
19 "are met:\n"
20 "\n"
21 " 1. Redistributions of source code must retain the above copyright\n"
22 " notice, this list of conditions and the following disclaimer.\n"
23 "\n"
24 " 2. Redistributions in binary form must reproduce the above copyright\n"
25 " notice, this list of conditions and the following disclaimer in\n"
26 " the documentation and/or other materials provided with the\n"
27 " distribution.\n"
28 "\n"
29 " 3. The name of the author may not be used to endorse or promote\n"
30 " products derived from this software without specific prior written\n"
31 " permission.\n"
32 "\n"
33 "THIS SOFTWARE IS PROVIDED BY THE AUTHOR \"AS IS\" AND ANY EXPRESS OR\n"
34 "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n"
35 "WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
36 "ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,\n"
37 "INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n"
38 "(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n"
39 "SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n"
40 "HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n"
41 "STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\n"
42 "IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n"
43 "POSSIBILITY OF SUCH DAMAGE.\n"
44 );
45 }
46
47
48 const char *sidutil_strip_hvsc_path(const char *hvscPath, const char *filename)
49 {
50 if (hvscPath != NULL)
51 {
52 const char *hvsc = hvscPath, *fptr = filename;
53
54 // Compare until end of string(s)
55 for (; *hvsc != 0 && *fptr != 0 && *hvsc == *fptr; hvsc++, fptr++);
56
57 // Full match?
58 if (*hvsc == 0)
59 return fptr - 1;
60 }
61
62 return filename;
63 }
64
65
66 char *sidutil_check_hvsc_file(const char *hvscPath, const char *filebase, const char *fext)
67 {
68 th_stat_data sdata;
69 char *npath = th_strdup_printf("%s%c%s%c%s%s",
70 hvscPath, TH_DIR_SEPARATOR_CHR,
71 SET_HVSC_DOCUMENTS, TH_DIR_SEPARATOR_CHR,
72 filebase, fext != NULL ? fext : "");
73
74 if (npath != NULL &&
75 th_stat_path(npath, &sdata) &&
76 (sdata.flags & TH_IS_READABLE) &&
77 (sdata.flags & TH_IS_DIR) == 0)
78 return npath;
79
80 th_free(npath);
81 return NULL;
82 }
83
84
85 #ifndef HAVE_ICONV
86
87 static const uint8_t sidutil_lang_iso88591_to_cp850[16*6] = {
88 0xff, 0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5, 0xf9, 0xb8, 0xa6, 0xae, 0xaa, 0xf0, 0xa9, 0xee,
89 0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa, 0xf7, 0xfb, 0xa7, 0xaf, 0xac, 0xab, 0xf3, 0xa8,
90 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8,
91 0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0x9e, 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0xe1,
92 0x85, 0xa0, 0x83, 0xc6, 0x84, 0x86, 0x91, 0x87, 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
93 0xd0, 0xa4, 0x95, 0xa2, 0x93, 0xe4, 0x94, 0xf6, 0x9b, 0x97, 0xa3, 0x96, 0x81, 0xec, 0xe7, 0x98,
94 };
95
96 static const uint8_t sidutil_lang_iso88591_to_cp437[16*6] = {
97 0xff, 0xad, 0x9b, 0x9c, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x00, 0xa6, 0xae, 0xaa, 0x00, 0x00, 0x00,
98 0xf8, 0xf1, 0xfd, 0x00, 0x00, 0xe6, 0x00, 0xfa, 0x00, 0x00, 0xa7, 0xaf, 0xac, 0xab, 0x00, 0xa8,
99 0x00, 0x00, 0x00, 0x00, 0x8e, 0x8f, 0x92, 0x80, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
100 0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0xe1,
101 0x85, 0xa0, 0x83, 0x00, 0x84, 0x86, 0x91, 0x87, 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
102 0x00, 0xa4, 0x95, 0xa2, 0x93, 0x00, 0x94, 0xf6, 0x00, 0x97, 0xa3, 0x96, 0x81, 0x00, 0x00, 0x98,
103 };
104
105
106 static char *sidutil_chconv_internal(SIDUtilChConvCtx *ctx, const char *src)
107 {
108 // Fallback conversion of ISO-8859-1 to X
109 const uint8_t *srcPtr = (const uint8_t *) src;
110 const uint8_t *convTable;
111 size_t outSize, outLen;
112 char *outBuf, outByte;
113
114 if (src == NULL)
115 return NULL;
116
117 outSize = strlen(src) + 1;
118 if ((outBuf = th_malloc(outSize)) == NULL)
119 return NULL;
120
121 for (outLen = 0; *srcPtr; srcPtr++)
122 {
123 switch (ctx->outLangID)
124 {
125 case TH_LANG_UTF8:
126 // Not 100% correct really, but close enough
127 if (*srcPtr < 0x80)
128 {
129 if (!th_strbuf_putch(&outBuf, &outSize, &outLen, *srcPtr))
130 goto err;
131 }
132 else
133 if (*srcPtr < 0xBF)
134 {
135 if (!th_strbuf_putch(&outBuf, &outSize, &outLen, 0xC2) ||
136 !th_strbuf_putch(&outBuf, &outSize, &outLen, *srcPtr))
137 goto err;
138 }
139 else
140 {
141 if (!th_strbuf_putch(&outBuf, &outSize, &outLen, 0xC3) ||
142 !th_strbuf_putch(&outBuf, &outSize, &outLen, *srcPtr - 0x40))
143 goto err;
144 }
145 break;
146
147 case TH_LANG_ISO88591:
148 if (!th_strbuf_putch(&outBuf, &outSize, &outLen, *srcPtr))
149 goto err;
150 break;
151
152 case TH_LANG_CP850:
153 case TH_LANG_CP437:
154 // Not 100% correct either, but close enough
155 convTable = (ctx->outLangID == TH_LANG_CP850) ?
156 sidutil_lang_iso88591_to_cp850 : sidutil_lang_iso88591_to_cp437;
157
158 if (*srcPtr < 0x7f)
159 outByte = *srcPtr;
160 else
161 if (*srcPtr >= 0xA0)
162 outByte = convTable[*srcPtr - 0xA0];
163 else
164 outByte = '?';
165
166 if (!th_strbuf_putch(&outBuf, &outSize, &outLen, outByte))
167 goto err;
168 break;
169 }
170 }
171
172 if (!th_strbuf_putch(&outBuf, &outSize, &outLen, *srcPtr))
173 goto err;
174
175 return outBuf;
176
177 err:
178 th_free(outBuf);
179 return NULL;
180 }
181
182 #endif
183
184
185 // NOTICE! Only call this function IF ctx->enabled == TRUE
186 char * sidutil_chconv_convert(SIDUtilChConvCtx *ctx, const char *src)
187 {
188 #ifdef HAVE_ICONV
189 size_t srcLeft = strlen(src) + 1;
190 size_t outLeft = srcLeft * 2;
191 char *srcPtr = (char *) src;
192 char *outBuf, *outPtr;
193
194 if ((outBuf = outPtr = th_malloc(outLeft + 1)) == NULL)
195 return NULL;
196
197 while (srcLeft > 0)
198 {
199 size_t ret = iconv(ctx->iconvCtx, &srcPtr, &srcLeft, &outPtr, &outLeft);
200 if (ret == (size_t) -1)
201 break;
202 }
203
204 return (char *) outBuf;
205 #else
206 return sidutil_chconv_internal(ctx, src);
207 #endif
208 }
209
210
211 int sidutil_chconv_init(SIDUtilChConvCtx *ctx, const char *outLang)
212 {
213 if (ctx == NULL)
214 return THERR_NULLPTR;
215
216 memset(ctx, 0, sizeof(*ctx));
217
218 if (outLang != NULL)
219 {
220 // Get the character encoding part (e.g. "UTF-8" etc.) and
221 // strip out and lowercase everything (e.g. "utf8")
222 size_t i;
223 char *ptr;
224
225 if ((ctx->outLang = th_strdup(outLang)) == NULL)
226 return THERR_MALLOC;
227
228 if ((ptr = strchr(ctx->outLang, '.')) == NULL)
229 ptr = ctx->outLang;
230 else
231 ptr++;
232
233 for (i = 0; *ptr; ptr++)
234 {
235 if (*ptr != '-')
236 ctx->outLang[i++] = th_tolower(*ptr);
237 }
238 ctx->outLang[i] = 0;
239
240 #ifdef HAVE_ICONV
241 // Initialize iconv, check if we have language/charset
242 ctx->iconvCtx = iconv_open(ctx->outLang, "iso88591");
243 ctx->enabled = (ctx->iconvCtx != (iconv_t) -1);
244 #else
245 // Check if we can use our fallback converter
246 if (strcmp(ctx->outLang, "utf8") == 0)
247 ctx->outLangID = TH_LANG_UTF8;
248 else
249 if (strcmp(ctx->outLang, "iso88591") == 0 ||
250 strcmp(ctx->outLang, "cp819") == 0 ||
251 strcmp(ctx->outLang, "latin1") == 0 ||
252 strcmp(ctx->outLang, "cp28591") == 0)
253 ctx->outLangID = TH_LANG_ISO88591;
254 else
255 if (strcmp(ctx->outLang, "cp850") == 0)
256 ctx->outLangID = TH_LANG_CP850;
257 else
258 if (strcmp(ctx->outLang, "cp437") == 0)
259 ctx->outLangID = TH_LANG_CP437;
260 else
261 ctx->outLangID = TH_LANG_ISO88591;
262
263 ctx->enabled = ctx->outLangID != TH_LANG_ISO88591;
264 #endif
265 }
266
267 return THERR_OK;
268 }
269
270
271 void sidutil_chconv_close(SIDUtilChConvCtx *ctx)
272 {
273 #ifdef HAVE_ICONV
274 if (ctx->iconvCtx != (iconv_t) -1)
275 iconv_close(ctx->iconvCtx);
276 #else
277 #endif
278
279 th_free(ctx->outLang);
280 }