|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516 |
- /*
- * cueprint.c -- print cd information based on a template
- *
- * Copyright (C) 2004, 2005, 2006, 2007 Svend Sorensen
- * For license terms, see the file COPYING in this distribution.
- */
-
- #include <ctype.h> /* isdigit() */
- #include <getopt.h> /* getopt_long() */
- #include <stdio.h> /* fprintf(), printf(), snprintf(), stderr */
- #include <stdlib.h> /* exit() */
- #include <string.h> /* strcasecmp() */
- #include "cuefile.h"
-
- #if HAVE_CONFIG_H
- #include "config.h"
- #else /* not HAVE_CONFIG_H */
- #define PACKAGE_STRING "cueprint"
- #endif /* HAVE_CONFIG_H */
-
- /* default templates */
-
- #define D_TEMPLATE "\
- Disc Information\n\
- arranger: %A\n\
- composer: %C\n\
- genre: %G\n\
- message: %M\n\
- no. of tracks: %N\n\
- performer: %P\n\
- songwriter: %S\n\
- title: %T\n\
- UPC/EAN: %U\n\
- "
-
- #define T_TEMPLATE "\
- \n\
- Track %n Information\n\
- arranger: %a\n\
- composer: %c\n\
- genre: %g\n\
- ISRC: %i\n\
- message: %m\n\
- track number: %n\n\
- perfomer: %p\n\
- title: %t\n\
- ISRC (CD-TEXT): %u\n\
- "
-
- /* default string to print for unset (NULL) values */
- #define VALUE_UNSET ""
-
- /*
- * *_get_* functions can return an int or a char *
- */
- typedef union {
- int ival;
- char *sval;
- char cval;
- } Value;
-
- char *progname;
-
- /* Print usage information and exit */
- void usage(int status)
- {
- if (0 == status) {
- printf("Usage: %s [option...] [file...]\n", progname);
- printf("Report disc and track information from a CUE or TOC file.\n"
- "\n"
- "OPTIONS\n"
- "-h, --help print usage\n"
- "-i, --input-format cue|toc set format of file(s)\n"
- "-n, --track-number <number> only print track information for single track\n"
- "-d, --disc-template <template> set disc template\n"
- "-t, --track-template <template> set track template\n"
- "-V, --version print version information\n"
- "\n"
- "Default disc template: %s\n"
- "Default track template: %s\n"
- "See the manual page for more information.\n",
- D_TEMPLATE, T_TEMPLATE);
- } else {
- fprintf(stderr, "Try `%s --help' for more information.\n", progname);
- }
-
- exit (status);
- }
-
- /* Print version information and exit */
- void version()
- {
- printf("%s\n", PACKAGE_STRING);
-
- exit(0);
- }
-
- /*
- * TODO: Shouldn't we be using vprintf() to help us out with this stuff?
- * (Branden Robinson)
- */
- void disc_field(char *conv, int length, Cd *cd, Value *value)
- {
- char *c; /* pointer to conversion character */
-
- Cdtext *cdtext = NULL;
- cdtext = cd_get_cdtext(cd);
-
- c = conv + length - 1;
-
- /*
- * After setting value, set *c to specify value type:
- * 'd' integer
- * 's' string
- * 'c' character
- */
- switch (*c) {
- case 'A':
- value->sval = cdtext_get(PTI_ARRANGER, cdtext);
- *c = 's';
- break;
- case 'C':
- value->sval = cdtext_get(PTI_COMPOSER, cdtext);
- *c = 's';
- break;
- case 'G':
- value->sval = cdtext_get(PTI_GENRE, cdtext);
- *c = 's';
- break;
- case 'M':
- value->sval = cdtext_get(PTI_MESSAGE, cdtext);
- *c = 's';
- break;
- case 'N':
- value->ival = cd_get_ntrack(cd);
- *c = 'd';
- break;
- case 'P':
- value->sval = cdtext_get(PTI_PERFORMER, cdtext);
- *c = 's';
- break;
- case 'R':
- value->sval = cdtext_get(PTI_ARRANGER, cdtext);
- *c = 's';
- break;
- case 'S':
- value->sval = cdtext_get(PTI_SONGWRITER, cdtext);
- *c = 's';
- break;
- case 'T':
- value->sval = cdtext_get(PTI_TITLE, cdtext);
- *c = 's';
- break;
- case 'U':
- value->sval = cdtext_get(PTI_UPC_ISRC, cdtext);
- *c = 's';
- break;
- default:
- value->cval = *c;
- *c = 'c';
- break;
- }
- }
-
- void track_field(char *conv, int length, Cd *cd, int trackno, Value *value)
- {
- char *c; /* pointer to conversion character */
-
- Track *track = NULL;
- Cdtext *cdtext = NULL;
-
- track = cd_get_track(cd, trackno);
- cdtext = track_get_cdtext(track);
-
- c = conv + length - 1;
-
- switch (*c) {
- case 'a':
- value->sval = cdtext_get(PTI_ARRANGER, cdtext);
- *c = 's';
- break;
- case 'c':
- value->sval = cdtext_get(PTI_COMPOSER, cdtext);
- *c = 's';
- break;
- case 'f':
- value->sval = track_get_filename(track);
- *c = 's';
- break;
- case 'g':
- value->sval = cdtext_get(PTI_GENRE, cdtext);
- *c = 's';
- break;
- case 'i':
- value->sval = track_get_isrc(track);
- *c = 's';
- break;
- case 'm':
- value->sval = cdtext_get(PTI_MESSAGE, cdtext);
- *c = 's';
- break;
- case 'n':
- value->ival = trackno;
- *c = 'd';
- break;
- case 'p':
- value->sval = cdtext_get(PTI_PERFORMER, cdtext);
- *c = 's';
- break;
- case 's':
- value->sval = cdtext_get(PTI_SONGWRITER, cdtext);
- *c = 's';
- break;
- case 't':
- value->sval = cdtext_get(PTI_TITLE, cdtext);
- *c = 's';
- break;
- case 'u':
- value->sval = cdtext_get(PTI_UPC_ISRC, cdtext);
- *c = 's';
- break;
- default:
- disc_field(conv, length, cd, value);
- break;
- }
-
- }
-
- /*
- * Print a conversion specification.
- * [flag(s)][width][.precision]<conversion-char>
- */
- void print_conv(char *start, int length, Cd *cd, int trackno)
- {
- char *conv; /* copy of conversion specification */
- Value value;
- char *c; /* pointer to conversion-char */
-
- /* TODO: use strndup? */
- conv = malloc((unsigned) (length + 1));
- strncpy(conv, start, length);
- conv[length] = '\0';
-
- /* conversion character */
- if (0 == trackno) {
- disc_field(conv, length, cd, &value);
- } else {
- track_field(conv, length, cd, trackno, &value);
- }
-
- c = conv + length - 1;
-
- switch (*c) {
- case 'c':
- printf(conv, value.cval);
- break;
- case 'd':
- printf(conv, value.ival);
- break;
- case 's':
- if (NULL == value.sval)
- printf(conv, VALUE_UNSET);
- else
- printf(conv, value.sval);
- break;
- default:
- printf("%zu: ", strlen(conv));
- printf("%s", conv);
- }
-
- free(conv);
- }
-
- void cd_printf(char *format, Cd *cd, int trackno)
- {
- char *c; /* pointer into format */
- char *conv_start;
- int conv_length;
-
- for (c = format; '\0' != *c; c++) {
- if ('%' == *c) {
- conv_start = c;
- conv_length = 1;
- c++;
-
- /* flags */
- while ( \
- '-' == *c \
- || '+' == *c \
- || ' ' == *c \
- || '0' == *c \
- || '#' == *c \
- ) {
- conv_length++;
- c++;
- }
-
- /* field width */
- /* '*' not recognized */
- while (0 != isdigit(*c)) {
- conv_length++;
- c++;
- }
-
- /* precision */
- /* '*' not recognized */
- if ('.' == *c) {
- conv_length++;
- c++;
-
- while (0 != isdigit(*c)) {
- conv_length++;
- c++;
- }
- }
-
- /* length modifier (h, l, or L) */
- /* not recognized */
-
- /* conversion character */
- conv_length++;
-
- print_conv(conv_start, conv_length, cd, trackno);
- } else {
- putchar(*c);
- }
- }
- }
-
- int info(char *name, int format, int trackno, char *d_template, char *t_template)
- {
- Cd *cd = NULL;
- int ntrack;
-
- if (NULL == (cd = cf_parse(name, &format))) {
- fprintf(stderr, "%s: error: unable to parse input file"
- " `%s'\n", progname, name);
- return -1;
- }
-
- ntrack = cd_get_ntrack(cd);
-
- if (-1 == trackno) {
- cd_printf(d_template, cd, 0);
-
- for (trackno = 1; trackno <= ntrack; trackno++) {
- cd_printf(t_template, cd, trackno);
- }
- } else if (0 == trackno) {
- cd_printf(d_template, cd, trackno);
- } else if (0 < trackno && ntrack >= trackno) {
- cd_printf(t_template, cd, trackno);
- } else {
- fprintf(stderr, "%s: error: track number out of range\n", progname);
- return -1;
- }
-
- return 0;
- }
-
- /*
- * Translate escape sequences in a string.
- * The string is overwritten and terminated.
- * TODO: this does not handle octal and hexidecimal escapes
- * except for \0
- */
- void translate_escapes(char *s)
- {
- char *read;
- char *write;
-
- read = s;
- write = s;
-
- while ('\0' != *read) {
- if ('\\' == *read) {
- read++;
-
- switch (*read) {
- case 'a':
- *write = '\a';
- break;
- case 'b':
- *write = '\b';
- break;
- case 'f':
- *write = '\f';
- break;
- case 'n':
- *write = '\n';
- break;
- case 'r':
- *write = '\r';
- break;
- case 't':
- *write = '\t';
- break;
- case 'v':
- *write = '\v';
- break;
- case '0':
- *write = '\0';
- break;
- default:
- /* ?, ', " are handled by the default */
- *write = *read;
- break;
- }
- } else {
- *write = *read;
- }
-
- read++;
- write++;
- }
-
- *write = '\0';
- }
-
- int main(int argc, char *argv[])
- {
- int format = UNKNOWN;
- int trackno = -1; /* track number (-1 = unspecified,
- 0 = disc info) */
- char *d_template = NULL; /* disc template */
- char *t_template = NULL; /* track template */
- int ret = 0; /* return value of info() */
-
- /* option variables */
- int c;
- /* getopt_long() variables */
- extern char *optarg;
- extern int optind;
-
- static struct option longopts[] = {
- {"help", no_argument, NULL, 'h'},
- {"input-format", required_argument, NULL, 'i'},
- {"track-number", required_argument, NULL, 'n'},
- {"disc-template", required_argument, NULL, 'd'},
- {"track-template", required_argument, NULL, 't'},
- {"version", no_argument, NULL, 'V'},
- {NULL, 0, NULL, 0}
- };
-
- progname = argv[0];
-
- while (-1 != (c = getopt_long(argc, argv, "hi:n:d:t:V", longopts, NULL))) {
- switch (c) {
- case 'h':
- usage(0);
- break;
- case 'i':
- if (0 == strcmp("cue", optarg)) {
- format = CUE;
- } else if (0 == strcmp("toc", optarg)) {
- format = TOC;
- } else {
- fprintf(stderr, "%s: error: unknown input file"
- " format `%s'\n", progname, optarg);
- usage(1);
- }
- break;
- case 'n':
- trackno = atoi(optarg);
- break;
- case 'd':
- d_template = optarg;
- break;
- case 't':
- t_template = optarg;
- break;
- case 'V':
- version();
- break;
- default:
- usage(1);
- break;
- }
- }
-
- /* If no disc or track template is set, use the defaults for both. */
- /* TODO: alternative to strdup to get variable strings? */
- if (NULL == d_template && NULL == t_template) {
- d_template = strdup(D_TEMPLATE);
- t_template = strdup(T_TEMPLATE);
- } else {
- if (NULL == d_template) {
- d_template = strdup("");
- }
-
- if (NULL == t_template) {
- t_template = strdup("");
- }
- }
-
- /* Translate escape sequences. */
- translate_escapes(d_template);
- translate_escapes(t_template);
-
- /* What we do depends on the number of operands. */
- if (optind == argc) {
- /* No operands: report information about stdin. */
- ret = info("-", format, trackno, d_template, t_template);
- } else {
- /* Report information about each operand. */
- for (; optind < argc; optind++) {
- ret = info(argv[optind], format, trackno, d_template, t_template);
- /* Exit if info() returns nonzero. */
- if (!ret) {
- break;
- }
- }
- }
-
- return ret;
- }
|