comparison sidinfo.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 ee56f1f2b528
children 71139ed7e43f
comparison
equal deleted inserted replaced
312:1950bb04a69b 313:b3d46806787d
6 #include "th_args.h" 6 #include "th_args.h"
7 #include "th_string.h" 7 #include "th_string.h"
8 #include "th_file.h" 8 #include "th_file.h"
9 #include "th_datastruct.h" 9 #include "th_datastruct.h"
10 #include "sidlib.h" 10 #include "sidlib.h"
11 #include "sidutil.h"
11 #include <sys/types.h> 12 #include <sys/types.h>
12 #include <dirent.h> 13 #include <dirent.h>
13 #ifdef HAVE_ICONV
14 # include <iconv.h>
15 #endif
16 14
17 15
18 // 16 //
19 // Some constants 17 // Some constants
20 // 18 //
21
22 // HVSC documents directory
23 #define SET_HVSC_DOCUMENTS "DOCUMENTS"
24
25 // Songlengths database filename prefix (.md5|.txt appended)
26 #define SET_SLDB_FILEBASE "Songlengths"
27
28 // STIL database file
29 #define SET_STILDB_FILENAME "STIL.txt"
30
31
32 enum 19 enum
33 { 20 {
34 OFMT_QUOTED = 0x0001, 21 OFMT_QUOTED = 0x0001,
35 OFMT_FORMAT = 0x0002, 22 OFMT_FORMAT = 0x0002,
36 }; 23 };
39 enum 26 enum
40 { 27 {
41 OTYPE_OTHER = 0, 28 OTYPE_OTHER = 0,
42 OTYPE_STR = 1, 29 OTYPE_STR = 1,
43 OTYPE_INT = 2, 30 OTYPE_INT = 2,
44 };
45
46
47 enum
48 {
49 TH_LANG_UTF8,
50 TH_LANG_ISO88591,
51 TH_LANG_CP850,
52 TH_LANG_CP437,
53 }; 31 };
54 32
55 33
56 typedef struct 34 typedef struct
57 { 35 {
133 PSFStack optFormat; 111 PSFStack optFormat;
134 112
135 SIDLibSLDB *sidSLDB = NULL; 113 SIDLibSLDB *sidSLDB = NULL;
136 SIDLibSTILDB *sidSTILDB = NULL; 114 SIDLibSTILDB *sidSTILDB = NULL;
137 115
138 116 SIDUtilChConvCtx setChConv;
139 BOOL setUseOutConv;
140 #ifdef HAVE_ICONV
141 iconv_t setIConvCtx;
142 #else
143 int setOutLang;
144 #endif
145 117
146 118
147 // Define option arguments 119 // Define option arguments
148 static const th_optarg optList[] = 120 static const th_optarg optList[] =
149 { 121 {
165 }; 137 };
166 138
167 static const int optListN = sizeof(optList) / sizeof(optList[0]); 139 static const int optListN = sizeof(optList) / sizeof(optList[0]);
168 140
169 141
170 void argShowLicense(void)
171 {
172 printf("%s - %s\n%s\n", th_prog_name, th_prog_desc, th_prog_author);
173 printf(
174 "\n"
175 "Redistribution and use in source and binary forms, with or without\n"
176 "modification, are permitted provided that the following conditions\n"
177 "are met:\n"
178 "\n"
179 " 1. Redistributions of source code must retain the above copyright\n"
180 " notice, this list of conditions and the following disclaimer.\n"
181 "\n"
182 " 2. Redistributions in binary form must reproduce the above copyright\n"
183 " notice, this list of conditions and the following disclaimer in\n"
184 " the documentation and/or other materials provided with the\n"
185 " distribution.\n"
186 "\n"
187 " 3. The name of the author may not be used to endorse or promote\n"
188 " products derived from this software without specific prior written\n"
189 " permission.\n"
190 "\n"
191 "THIS SOFTWARE IS PROVIDED BY THE AUTHOR \"AS IS\" AND ANY EXPRESS OR\n"
192 "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n"
193 "WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
194 "ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,\n"
195 "INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n"
196 "(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n"
197 "SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n"
198 "HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n"
199 "STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\n"
200 "IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n"
201 "POSSIBILITY OF SUCH DAMAGE.\n"
202 );
203 }
204
205
206 void argShowHelp(void) 142 void argShowHelp(void)
207 { 143 {
208 int index, len; 144 int index, len;
209 145
210 th_print_banner(stdout, th_prog_name, "[options] <sid filename> [sid filename #2 ..]"); 146 th_print_banner(stdout, th_prog_name, "[options] <sid file|path> [file|path #2 ..]");
211 th_args_help(stdout, optList, optListN, 0); 147 th_args_help(stdout, optList, optListN, 0);
212 printf( 148 printf(
213 "\n" 149 "\n"
214 "Available fields:\n"); 150 "Available fields:\n");
215 151
279 } 215 }
280 return found; 216 return found;
281 } 217 }
282 218
283 219
284 const char *siStripHVSCPath(const char *filename)
285 {
286 if (setHVSCPath != NULL)
287 {
288 const char *hvsc = setHVSCPath, *fptr = filename;
289
290 // Compare until end of string(s)
291 for (; *hvsc != 0 && *fptr != 0 && *hvsc == *fptr; hvsc++, fptr++);
292
293 // Full match?
294 if (*hvsc == 0)
295 return fptr - 1;
296 }
297
298 return filename;
299 }
300
301
302 char *siCheckHVSCFilePath(const char *filebase, const char *fext)
303 {
304 th_stat_data sdata;
305 char *npath = th_strdup_printf("%s%c%s%c%s%s",
306 setHVSCPath, TH_DIR_SEPARATOR_CHR,
307 SET_HVSC_DOCUMENTS, TH_DIR_SEPARATOR_CHR,
308 filebase, fext != NULL ? fext : "");
309
310 if (npath != NULL &&
311 th_stat_path(npath, &sdata) &&
312 (sdata.flags & TH_IS_READABLE) &&
313 (sdata.flags & TH_IS_DIR) == 0)
314 return npath;
315
316 th_free(npath);
317 return NULL;
318 }
319
320
321 BOOL siStackAddItem(PSFStack *stack, const PSFStackItem *item) 220 BOOL siStackAddItem(PSFStack *stack, const PSFStackItem *item)
322 { 221 {
323 if (stack->items == NULL || stack->nitems + 1 >= stack->nallocated) 222 if (stack->items == NULL || stack->nitems + 1 >= stack->nallocated)
324 { 223 {
325 stack->nallocated += 16; 224 stack->nallocated += 16;
391 290
392 start = end + 1; 291 start = end + 1;
393 } 292 }
394 293
395 return TRUE; 294 return TRUE;
396 }
397
398
399 #ifndef HAVE_ICONV
400
401 static const uint8_t si_lang_iso88591_to_cp850[16*6] = {
402 0xff, 0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5, 0xf9, 0xb8, 0xa6, 0xae, 0xaa, 0xf0, 0xa9, 0xee,
403 0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa, 0xf7, 0xfb, 0xa7, 0xaf, 0xac, 0xab, 0xf3, 0xa8,
404 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8,
405 0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0x9e, 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0xe1,
406 0x85, 0xa0, 0x83, 0xc6, 0x84, 0x86, 0x91, 0x87, 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
407 0xd0, 0xa4, 0x95, 0xa2, 0x93, 0xe4, 0x94, 0xf6, 0x9b, 0x97, 0xa3, 0x96, 0x81, 0xec, 0xe7, 0x98,
408 };
409
410 static const uint8_t si_lang_iso88591_to_cp437[16*6] = {
411 0xff, 0xad, 0x9b, 0x9c, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x00, 0xa6, 0xae, 0xaa, 0x00, 0x00, 0x00,
412 0xf8, 0xf1, 0xfd, 0x00, 0x00, 0xe6, 0x00, 0xfa, 0x00, 0x00, 0xa7, 0xaf, 0xac, 0xab, 0x00, 0xa8,
413 0x00, 0x00, 0x00, 0x00, 0x8e, 0x8f, 0x92, 0x80, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
414 0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0xe1,
415 0x85, 0xa0, 0x83, 0x00, 0x84, 0x86, 0x91, 0x87, 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
416 0x00, 0xa4, 0x95, 0xa2, 0x93, 0x00, 0x94, 0xf6, 0x00, 0x97, 0xa3, 0x96, 0x81, 0x00, 0x00, 0x98,
417 };
418
419 #endif
420
421
422 char *siConvertCharset(const char *src)
423 {
424 #ifdef HAVE_ICONV
425 size_t srcLeft = strlen(src) + 1;
426 size_t outLeft = srcLeft * 2;
427 char *srcPtr = (char *) src;
428 char *outBuf, *outPtr;
429
430 if ((outBuf = outPtr = th_malloc(outLeft + 1)) == NULL)
431 return NULL;
432
433 while (srcLeft > 0)
434 {
435 size_t ret = iconv(setIConvCtx, &srcPtr, &srcLeft, &outPtr, &outLeft);
436 if (ret == (size_t) -1)
437 break;
438 }
439
440 #else
441 // Fallback conversion of ISO-8859-1 to X
442 size_t srcSize = strlen(src), outSize, minLeft;
443 const uint8_t *srcPtr = (const uint8_t *) src;
444 const uint8_t *tab;
445 uint8_t *outBuf, *outPtr;
446
447 switch (setOutLang)
448 {
449 case TH_LANG_UTF8:
450 outSize = srcSize * 2;
451 minLeft = 2;
452 break;
453
454 default:
455 outSize = srcSize;
456 minLeft = 1;
457 }
458
459 if ((outBuf = outPtr = th_malloc(outSize)) == NULL)
460 return NULL;
461
462 while (srcSize > 0 && outSize >= minLeft)
463 {
464 switch (setOutLang)
465 {
466 case TH_LANG_UTF8:
467 // Not 100% correct really, but close enough
468 if (*srcPtr < 0x80)
469 {
470 *outPtr++ = *srcPtr;
471 outSize--;
472 }
473 else
474 if (*srcPtr < 0xBF)
475 {
476 *outPtr++ = 0xC2;
477 *outPtr++ = *srcPtr;
478 outSize -= 2;
479 }
480 else
481 {
482 *outPtr++ = 0xC3;
483 *outPtr++ = *srcPtr - 0x40;
484 outSize -= 2;
485 }
486 break;
487
488 case TH_LANG_ISO88591:
489 *outPtr++ = *srcPtr;
490 outSize--;
491 break;
492
493 case TH_LANG_CP850:
494 case TH_LANG_CP437:
495 // Not 100% correct either, but close enough
496 tab = (setOutLang == TH_LANG_CP850) ? si_lang_iso88591_to_cp850 : si_lang_iso88591_to_cp437;
497
498 if (*srcPtr < 0x7f)
499 *outPtr++ = *srcPtr;
500 else
501 if (*srcPtr >= 0xA0)
502 *outPtr++ = tab[*srcPtr - 0xA0];
503 else
504 *outPtr++ = '?';
505
506 outSize--;
507 break;
508 }
509
510 srcPtr++;
511 srcSize--;
512 }
513
514 *outPtr++ = 0;
515 #endif
516
517 return (char *) outBuf;
518 } 295 }
519 296
520 297
521 static int siItemFormatStrPutInt(th_vprintf_ctx *ctx, th_vprintf_putch vputch, 298 static int siItemFormatStrPutInt(th_vprintf_ctx *ctx, th_vprintf_putch vputch,
522 const int value, const int f_radix, int f_flags, int f_width, int f_prec, 299 const int value, const int f_radix, int f_flags, int f_width, int f_prec,
855 argShowHelp(); 632 argShowHelp();
856 exit(0); 633 exit(0);
857 break; 634 break;
858 635
859 case 10: 636 case 10:
860 argShowLicense(); 637 sidutil_print_license();
861 exit(0); 638 exit(0);
862 break; 639 break;
863 640
864 case 1: 641 case 1:
865 th_verbosity++; 642 th_verbosity++;
1038 const char *d_str, const int d_int, 815 const char *d_str, const int d_int,
1039 const BOOL convert) 816 const BOOL convert)
1040 { 817 {
1041 char *str, *tmp; 818 char *str, *tmp;
1042 819
1043 if (setUseOutConv && d_str != NULL && convert) 820 if (d_str != NULL && setChConv.enabled && convert)
1044 { 821 {
1045 char *tmp2 = siConvertCharset(d_str); 822 char *tmp2 = sidutil_chconv_convert(&setChConv, d_str);
1046 tmp = siEscapeString(tmp2, optEscapeChars); 823 tmp = siEscapeString(tmp2, optEscapeChars);
1047 th_free(tmp2); 824 th_free(tmp2);
1048 } 825 }
1049 else 826 else
1050 tmp = siEscapeString(d_str, optEscapeChars); 827 tmp = siEscapeString(d_str, optEscapeChars);
1280 psid->lengths = sidlib_sldb_get_by_hash(sidSLDB, psid->hash); 1057 psid->lengths = sidlib_sldb_get_by_hash(sidSLDB, psid->hash);
1281 1058
1282 // Get STIL information, if any 1059 // Get STIL information, if any
1283 if (sidSTILDB != NULL) 1060 if (sidSTILDB != NULL)
1284 { 1061 {
1285 psid->stil = sidlib_stildb_get_node(sidSTILDB, siStripHVSCPath(filename)); 1062 psid->stil = sidlib_stildb_get_node(sidSTILDB,
1063 sidutil_strip_hvsc_path(setHVSCPath, filename));
1064
1286 if (psid->stil != NULL) 1065 if (psid->stil != NULL)
1287 psid->stil->lengths = psid->lengths; 1066 psid->stil->lengths = psid->lengths;
1288 } 1067 }
1289 1068
1290 // Output 1069 // Output
1422 } 1201 }
1423 1202
1424 1203
1425 int main(int argc, char *argv[]) 1204 int main(int argc, char *argv[])
1426 { 1205 {
1427 char *setLang = th_strdup(getenv("LANG"));
1428 th_ioctx *inFile = NULL; 1206 th_ioctx *inFile = NULL;
1207 char *setLang = getenv("LANG");
1208 int ret;
1429 1209
1430 // Get HVSC_BASE env variable if it is set 1210 // Get HVSC_BASE env variable if it is set
1431 th_pstr_cpy(&setHVSCPath, getenv("HVSC_BASE")); 1211 th_pstr_cpy(&setHVSCPath, getenv("HVSC_BASE"));
1432 1212
1433 // Initialize 1213 // Initialize
1437 1217
1438 th_verbosity = 0; 1218 th_verbosity = 0;
1439 1219
1440 memset(&optFormat, 0, sizeof(optFormat)); 1220 memset(&optFormat, 0, sizeof(optFormat));
1441 1221
1442 // Get environment language 1222 // Initialize character conversion
1443 if (setLang != NULL) 1223 if ((ret = sidutil_chconv_init(&setChConv, setLang)) != THERR_OK)
1444 { 1224 {
1445 // Get the character encoding part (e.g. "UTF-8" etc.) and 1225 THERR("Could not initialize character set conversion (LANG='%s'): %s\n",
1446 // strip out and lowercase everything (e.g. "utf8") 1226 setLang, th_error_str(ret));
1447 size_t i;
1448 char *ptr = strchr(setLang, '.');
1449 ptr = (ptr == NULL) ? setLang : ptr + 1;
1450
1451 for (i = 0; *ptr; ptr++)
1452 {
1453 if (*ptr != '-')
1454 setLang[i++] = th_tolower(*ptr);
1455 }
1456 setLang[i] = 0;
1457
1458 #ifdef HAVE_ICONV
1459 // Initialize iconv, check if we have language/charset
1460 setIConvCtx = iconv_open("utf8", "iso88591");
1461 setUseOutConv = setIConvCtx != (iconv_t) -1;
1462 #else
1463 // Check if we can use our fallback converter
1464 if (strcmp(setLang, "utf8") == 0)
1465 setOutLang = TH_LANG_UTF8;
1466 else
1467 if (strcmp(setLang, "iso88591") == 0 ||
1468 strcmp(setLang, "cp819") == 0 ||
1469 strcmp(setLang, "latin1") == 0 ||
1470 strcmp(setLang, "cp28591") == 0)
1471 setOutLang = TH_LANG_ISO88591;
1472 else
1473 if (strcmp(setLang, "cp850") == 0)
1474 setOutLang = TH_LANG_CP850;
1475 else
1476 if (strcmp(setLang, "cp437") == 0)
1477 setOutLang = TH_LANG_CP437;
1478 else
1479 setOutLang = TH_LANG_ISO88591;
1480
1481 setUseOutConv = setOutLang != TH_LANG_ISO88591;
1482 #endif
1483 } 1227 }
1484 1228
1485 // Parse command line arguments 1229 // Parse command line arguments
1486 if (!th_args_process(argc, argv, optList, optListN, 1230 if (!th_args_process(argc, argv, optList, optListN,
1487 argHandleOpt, NULL, OPTH_ONLY_OPTS)) 1231 argHandleOpt, NULL, OPTH_ONLY_OPTS))
1488 goto out; 1232 goto out;
1489 1233
1490 THMSG(2, "Requested output LANG='%s', use charset conversion=%s\n", 1234 THMSG(2, "Requested output LANG='%s', use charset conversion=%s\n",
1491 setLang, setUseOutConv ? "yes" : "no"); 1235 setChConv.outLang, setChConv.enabled ? "yes" : "no");
1492 1236
1493 if (optOneLineFieldSep != NULL || 1237 if (optOneLineFieldSep != NULL ||
1494 (!optFieldOutput && optFormat.nitems > 0)) 1238 (!optFieldOutput && optFormat.nitems > 0))
1495 { 1239 {
1496 // For one-line format and formatted output (-F), disable parsable 1240 // For one-line format and formatted output (-F), disable parsable
1523 if (th_strrcasecmp(setHVSCPath, TH_DIR_SEPARATOR_STR) == NULL) 1267 if (th_strrcasecmp(setHVSCPath, TH_DIR_SEPARATOR_STR) == NULL)
1524 th_pstr_printf(&setHVSCPath, "%s%c", setHVSCPath, TH_DIR_SEPARATOR_CHR); 1268 th_pstr_printf(&setHVSCPath, "%s%c", setHVSCPath, TH_DIR_SEPARATOR_CHR);
1525 1269
1526 // If SLDB path is not set, autocheck for .md5 and .txt 1270 // If SLDB path is not set, autocheck for .md5 and .txt
1527 if (setSLDBPath == NULL) 1271 if (setSLDBPath == NULL)
1528 setSLDBPath = siCheckHVSCFilePath(SET_SLDB_FILEBASE, ".md5"); 1272 setSLDBPath = sidutil_check_hvsc_file(setHVSCPath, SET_SLDB_FILEBASE, ".md5");
1529 1273
1530 if (setSLDBPath == NULL) 1274 if (setSLDBPath == NULL)
1531 setSLDBPath = siCheckHVSCFilePath(SET_SLDB_FILEBASE, ".txt"); 1275 setSLDBPath = sidutil_check_hvsc_file(setHVSCPath, SET_SLDB_FILEBASE, ".txt");
1532 1276
1533 if (setSTILDBPath == NULL) 1277 if (setSTILDBPath == NULL)
1534 setSTILDBPath = siCheckHVSCFilePath(SET_STILDB_FILENAME, NULL); 1278 setSTILDBPath = sidutil_check_hvsc_file(setHVSCPath, SET_STILDB_FILENAME, NULL);
1535 } 1279 }
1536 1280
1537 if (setSLDBPath != NULL) 1281 if (setSLDBPath != NULL)
1538 { 1282 {
1539 // Initialize SLDB 1283 // Initialize SLDB
1540 int ret = THERR_OK;
1541
1542 setSLDBNewFormat = th_strrcasecmp(setSLDBPath, ".md5") != NULL; 1284 setSLDBNewFormat = th_strrcasecmp(setSLDBPath, ".md5") != NULL;
1543 1285
1544 if ((ret = th_io_fopen(&inFile, &th_stdio_io_ops, setSLDBPath, "r")) != THERR_OK) 1286 if ((ret = th_io_fopen(&inFile, &th_stdio_io_ops, setSLDBPath, "r")) != THERR_OK)
1545 { 1287 {
1546 THERR("Could not open SLDB '%s': %s\n", 1288 THERR("Could not open SLDB '%s': %s\n",
1582 } 1324 }
1583 1325
1584 if (setSTILDBPath != NULL) 1326 if (setSTILDBPath != NULL)
1585 { 1327 {
1586 // Initialize STILDB 1328 // Initialize STILDB
1587 int ret = THERR_OK;
1588
1589 if ((ret = th_io_fopen(&inFile, &th_stdio_io_ops, setSTILDBPath, "r")) != THERR_OK) 1329 if ((ret = th_io_fopen(&inFile, &th_stdio_io_ops, setSTILDBPath, "r")) != THERR_OK)
1590 { 1330 {
1591 THERR("Could not open STIL database '%s': %s\n", 1331 THERR("Could not open STIL database '%s': %s\n",
1592 setSTILDBPath, th_error_str(ret)); 1332 setSTILDBPath, th_error_str(ret));
1593 goto err2; 1333 goto err2;
1634 THERR("No filename(s) specified. Try --help.\n"); 1374 THERR("No filename(s) specified. Try --help.\n");
1635 } 1375 }
1636 1376
1637 out: 1377 out:
1638 1378
1639 #ifdef HAVE_ICONV 1379 sidutil_chconv_close(&setChConv);
1640 if (setUseOutConv)
1641 iconv_close(setIConvCtx);
1642 #endif
1643
1644 th_free(setLang);
1645 1380
1646 siClearStack(&optFormat); 1381 siClearStack(&optFormat);
1647 th_free(setHVSCPath); 1382 th_free(setHVSCPath);
1648 th_free(setSLDBPath); 1383 th_free(setSLDBPath);
1649 th_free(setSTILDBPath); 1384 th_free(setSTILDBPath);