comparison sidinfo.c @ 147:f636c55c7cc4

Implement C printf-style format specifiers for -F option @fields@.
author Matti Hamalainen <ccr@tnsp.org>
date Wed, 25 Oct 2017 23:20:09 +0300
parents 263cd25749a1
children 01cde57da297
comparison
equal deleted inserted replaced
146:263cd25749a1 147:f636c55c7cc4
17 #define SET_SLDB_FILENAME "Songlengths.txt" 17 #define SET_SLDB_FILENAME "Songlengths.txt"
18 18
19 19
20 enum 20 enum
21 { 21 {
22 OFMT_FORMAT = 0x0002,
23 };
24
25
26 enum
27 {
22 OTYPE_OTHER = 0, 28 OTYPE_OTHER = 0,
23 OTYPE_STR = 1, 29 OTYPE_STR = 1,
24 OTYPE_INT = 2, 30 OTYPE_INT = 2,
25 }; 31 };
26 32
29 { 35 {
30 int cmd; 36 int cmd;
31 char *str; 37 char *str;
32 char chr; 38 char chr;
33 int flags; 39 int flags;
40 char *fmt;
34 } PSFStackItem; 41 } PSFStackItem;
35 42
36 43
37 typedef struct 44 typedef struct
38 { 45 {
149 "\n" 156 "\n"
150 "Format strings for '-F' option are composed of @fields@ that\n" 157 "Format strings for '-F' option are composed of @fields@ that\n"
151 "are expanded to their value. Also, escape sequences \\r, \\n and \\t\n" 158 "are expanded to their value. Also, escape sequences \\r, \\n and \\t\n"
152 "can be used: -F \"hash=@hash@\\ncopy=@copyright@\\n\"\n" 159 "can be used: -F \"hash=@hash@\\ncopy=@copyright@\\n\"\n"
153 "\n" 160 "\n"
161 "The -F fields can be further formatted via printf-style specifiers:\n"
162 "-F \"@copyright:'%%-30s'@\"\n"
163 "\n"
154 "When specifying HVSC path, it is preferable to use -H/--hvsc option,\n" 164 "When specifying HVSC path, it is preferable to use -H/--hvsc option,\n"
155 "as STIL.txt and Songlengths.txt will be automatically used from there.\n" 165 "as STIL.txt and Songlengths.txt will be automatically used from there.\n"
156 , th_prog_name); 166 , th_prog_name);
157 } 167 }
158 168
264 274
265 return TRUE; 275 return TRUE;
266 } 276 }
267 277
268 278
279 int siItemFormatStrPutInt(th_vprintf_ctx *ctx, th_vprintf_putch vputch,
280 const int value, const int f_radix, int f_flags, int f_width, int f_prec,
281 const BOOL f_unsig, char *(f_alt)(const char *buf, const size_t blen, const int vret, const int flags))
282 {
283 char buf[64];
284 int f_len = 0, vret;
285 BOOL f_neg = FALSE;
286
287 vret = th_vprintf_buf_int(buf, sizeof(buf), &f_len, value,
288 f_radix, f_flags & TH_PF_UPCASE, f_unsig, &f_neg);
289
290 if (vret == EOF)
291 return 0;
292
293 return th_vprintf_put_int_format(ctx, vputch, buf, f_flags, f_width, f_prec, f_len, vret, f_neg, f_unsig, f_alt);
294 }
295
296
297 int siItemFormatStrPrintDo(th_vprintf_ctx *ctx, th_vprintf_putch vputch, const char *fmt,
298 const PSFOption *opt, const char *d_str, const int d_int)
299 {
300 int ret = 0;
301
302 while (*fmt)
303 {
304 if (*fmt != '%')
305 {
306 if ((ret = vputch(ctx, *fmt)) == EOF)
307 goto out;
308 }
309 else
310 {
311 int f_width = -1, f_prec = -1, f_flags = 0;
312 BOOL end = FALSE;
313
314 fmt++;
315
316 // Check for flags
317 while (!end)
318 {
319 switch (*fmt)
320 {
321 case '#':
322 f_flags |= TH_PF_ALT;
323 break;
324
325 case '+':
326 f_flags |= TH_PF_SIGN;
327 break;
328
329 case '0':
330 f_flags |= TH_PF_ZERO;
331 break;
332
333 case '-':
334 f_flags |= TH_PF_LEFT;
335 break;
336
337 case ' ':
338 f_flags |= TH_PF_SPACE;
339 break;
340
341 case '\'':
342 f_flags |= TH_PF_GROUP;
343 break;
344
345 default:
346 end = TRUE;
347 break;
348 }
349 if (!end) fmt++;
350 }
351
352 // Get field width
353 if (*fmt == '*')
354 {
355 return -101;
356 }
357 else
358 {
359 f_width = 0;
360 while (th_isdigit(*fmt))
361 f_width = f_width * 10 + (*fmt++ - '0');
362 }
363
364 // Check for field precision
365 if (*fmt == '.')
366 {
367 fmt++;
368 if (*fmt == '*')
369 {
370 return -102;
371 }
372 else
373 {
374 // If no digit after '.', precision is to be 0
375 f_prec = 0;
376 while (th_isdigit(*fmt))
377 f_prec = f_prec * 10 + (*fmt++ - '0');
378 }
379 }
380
381
382 // Check for length modifiers (only some are supported currently)
383 switch (*fmt)
384 {
385 case 0:
386 return -104;
387
388 case 'o':
389 if (opt->type != OTYPE_INT) return -120;
390 if ((ret = siItemFormatStrPutInt(ctx, vputch, d_int, 8, f_flags, f_width, f_prec, TRUE, th_vprintf_altfmt_oct)) == EOF)
391 goto out;
392 break;
393
394 case 'u':
395 case 'i':
396 case 'd':
397 if (opt->type != OTYPE_INT) return -120;
398 if ((ret = siItemFormatStrPutInt(ctx, vputch, d_int, 10, f_flags, f_width, f_prec, *fmt == 'u', NULL)) == EOF)
399 goto out;
400 break;
401
402 case 'x':
403 case 'X':
404 if (opt->type != OTYPE_INT) return -120;
405 if (*fmt == 'X')
406 f_flags |= TH_PF_UPCASE;
407 if ((ret = siItemFormatStrPutInt(ctx, vputch, d_int, 16, f_flags, f_width, f_prec, TRUE, th_vprintf_altfmt_hex)) == EOF)
408 goto out;
409 break;
410
411 case 's':
412 if (opt->type != OTYPE_STR) return -121;
413 if ((ret = th_vprintf_put_str(ctx, vputch, d_str, f_flags, f_width, f_prec)) == EOF)
414 goto out;
415 break;
416
417 //case '%':
418 default:
419 if ((ret = vputch(ctx, *fmt)) == EOF)
420 goto out;
421 break;
422 }
423 }
424 fmt++;
425 }
426
427 out:
428 return ret == EOF ? ret : ctx->ipos;
429 }
430
431
432 static int siItemFormatStrPutCH(th_vprintf_ctx *ctx, const char ch)
433 {
434 if (ctx->pos + 1 >= ctx->size)
435 {
436 ctx->size += 64;
437 if ((ctx->buf = th_realloc(ctx->buf, ctx->size)) == NULL)
438 return EOF;
439 }
440
441 ctx->buf[ctx->pos] = ch;
442
443 ctx->pos++;
444 ctx->ipos++;
445 return ch;
446 }
447
448
449 char * siItemFormatStrPrint(const char *fmt, const PSFOption *opt, const char *d_str, const int d_int)
450 {
451 th_vprintf_ctx ctx;
452
453 ctx.size = 128;
454 ctx.buf = th_malloc(ctx.size);
455 ctx.pos = 0;
456 ctx.ipos = 0;
457
458 if (ctx.buf == NULL)
459 return NULL;
460
461 if (siItemFormatStrPrintDo(&ctx, siItemFormatStrPutCH, fmt, opt, d_str, d_int) <= 0)
462 goto err;
463
464 if (siItemFormatStrPutCH(&ctx, 0) < 0)
465 goto err;
466
467 return ctx.buf;
468
469 err:
470 th_free(ctx.buf);
471 return NULL;
472 }
473
474
475 static int siItemFormatStrPutCHNone(th_vprintf_ctx *ctx, const char ch)
476 {
477 ctx->pos++;
478 ctx->ipos++;
479 return ch;
480 }
481
482
483 BOOL siItemFormatStrCheck(const char *fmt, const PSFOption *opt)
484 {
485 th_vprintf_ctx ctx;
486
487 memset(&ctx, 0, sizeof(ctx));
488
489 return siItemFormatStrPrintDo(&ctx, siItemFormatStrPutCHNone, fmt, opt, NULL, 0) >= 0;
490 }
491
492
269 // 493 //
270 // Parse a format string into a PSFStack structure 494 // Parse a format string into a PSFStack structure
271 // 495 //
272 BOOL argParsePSFormatStr(PSFStack *stack, const char *fmt) 496 BOOL argParsePSFormatStr(PSFStack *stack, const char *fmt)
273 { 497 {
312 if (!siStackAddItem(stack, &item)) 536 if (!siStackAddItem(stack, &item))
313 return FALSE; 537 return FALSE;
314 } 538 }
315 else 539 else
316 { 540 {
317 char *field = th_strndup_trim(start, fmt - start, TH_TRIM_BOTH); 541 char *fopt = NULL, *pfield, *field = th_strndup_trim(start, fmt - start, TH_TRIM_BOTH);
542 if ((pfield = strchr(field, ':')) != NULL)
543 {
544 *pfield = 0;
545 fopt = th_strdup_trim(pfield + 1, TH_TRIM_BOTH);
546 }
318 547
319 int ret = argMatchPSFieldError(field); 548 int ret = argMatchPSFieldError(field);
320 if (ret >= 0) 549 if (ret >= 0)
321 { 550 {
322 item.cmd = ret; 551 item.cmd = ret;
323 item.flags = 0; 552 item.flags = 0;
553 item.fmt = NULL;
324 item.str = NULL; 554 item.str = NULL;
555
556 if (fopt != NULL)
557 {
558 if (siItemFormatStrCheck(fopt, &optPSOptions[item.cmd]))
559 {
560 item.flags |= OFMT_FORMAT;
561 item.fmt = th_strdup(fopt);
562 }
563 else
564 {
565 THERR("Invalid field format specifier '%s' in '%s'.\n", fopt, field);
566 rval = FALSE;
567 }
568 }
325 569
326 if (!siStackAddItem(stack, &item)) 570 if (!siStackAddItem(stack, &item))
327 rval = FALSE; 571 rval = FALSE;
328 } 572 }
329 else 573 else
330 rval = FALSE; 574 rval = FALSE;
331 575
576 th_free(fopt);
332 th_free(field); 577 th_free(field);
333 } 578 }
334 579
335 mode = 0; 580 mode = 0;
336 fmt++; 581 fmt++;
450 695
451 696
452 static void siPrintPSIDInfoLine(FILE *outFile, BOOL *shown, const PSFStackItem *item, const char *d_str, const int d_int) 697 static void siPrintPSIDInfoLine(FILE *outFile, BOOL *shown, const PSFStackItem *item, const char *d_str, const int d_int)
453 { 698 {
454 const PSFOption *opt = &optPSOptions[item->cmd]; 699 const PSFOption *opt = &optPSOptions[item->cmd];
455 char *str = NULL; 700 char *fmt, *str = NULL;
456 701
457 switch (opt->type) 702 switch (opt->type)
458 { 703 {
459 case OTYPE_INT: 704 case OTYPE_INT:
460 str = th_strdup_printf(optHexadecimal ? "$%04x" : "%d", d_int); 705 if (item->flags & OFMT_FORMAT)
706 fmt = item->fmt;
707 else
708 fmt = optHexadecimal ? "$%04x" : "%d";
709
710 str = siItemFormatStrPrint(fmt, opt, d_str, d_int);
461 break; 711 break;
462 712
463 case OTYPE_STR: 713 case OTYPE_STR:
464 str = th_strdup(d_str); 714 if (item->flags & OFMT_FORMAT)
715 fmt = item->fmt;
716 else
717 fmt = "%s";
718
719 str = siItemFormatStrPrint(fmt, opt, d_str, d_int);
465 break; 720 break;
466 } 721 }
467 722
468 siPrintFieldPrefix(outFile, opt); 723 siPrintFieldPrefix(outFile, opt);
469 724
579 BOOL argHandleFile(char *filename) 834 BOOL argHandleFile(char *filename)
580 { 835 {
581 PSIDHeader *psid = NULL; 836 PSIDHeader *psid = NULL;
582 th_ioctx *inFile = NULL; 837 th_ioctx *inFile = NULL;
583 FILE *outFile; 838 FILE *outFile;
584 int index;
585 BOOL shown = FALSE; 839 BOOL shown = FALSE;
586 840
587 optNFiles++; 841 optNFiles++;
588 outFile = stdout; 842 outFile = stdout;
589 843
603 // Get songlength information, if any 857 // Get songlength information, if any
604 if (sidSLDB != NULL) 858 if (sidSLDB != NULL)
605 psid->lengths = si_sldb_get_by_hash(sidSLDB, psid->hash); 859 psid->lengths = si_sldb_get_by_hash(sidSLDB, psid->hash);
606 860
607 // Output 861 // Output
608 for (index = 0; index < optFormat.nitems; index++) 862 for (int index = 0; index < optFormat.nitems; index++)
609 { 863 {
610 PSFStackItem *item = &optFormat.items[index]; 864 PSFStackItem *item = &optFormat.items[index];
611 switch (item->cmd) 865 switch (item->cmd)
612 { 866 {
613 case -1: 867 case -1: