Mercurial > hg > sidinfo
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 } |