You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

517 lines
10 KiB

  1. /*
  2. * cueprint.c -- print cd information based on a template
  3. *
  4. * Copyright (C) 2004, 2005, 2006, 2007, 2013 Svend Sorensen
  5. * For license terms, see the file COPYING in this distribution.
  6. */
  7. #include <ctype.h> /* isdigit() */
  8. #include <getopt.h> /* getopt_long() */
  9. #include <stdio.h> /* fprintf(), printf(), snprintf(), stderr */
  10. #include <stdlib.h> /* exit() */
  11. #include <string.h> /* strcasecmp() */
  12. #include "cuefile.h"
  13. #if HAVE_CONFIG_H
  14. #include "config.h"
  15. #else /* not HAVE_CONFIG_H */
  16. #define PACKAGE_STRING "cueprint"
  17. #endif /* HAVE_CONFIG_H */
  18. /* default templates */
  19. #define D_TEMPLATE "\
  20. Disc Information\n\
  21. arranger: %A\n\
  22. composer: %C\n\
  23. genre: %G\n\
  24. message: %M\n\
  25. no. of tracks: %N\n\
  26. performer: %P\n\
  27. songwriter: %S\n\
  28. title: %T\n\
  29. UPC/EAN: %U\n\
  30. "
  31. #define T_TEMPLATE "\
  32. \n\
  33. Track %n Information\n\
  34. arranger: %a\n\
  35. composer: %c\n\
  36. genre: %g\n\
  37. ISRC: %i\n\
  38. message: %m\n\
  39. track number: %n\n\
  40. perfomer: %p\n\
  41. title: %t\n\
  42. ISRC (CD-TEXT): %u\n\
  43. "
  44. /* default string to print for unset (NULL) values */
  45. #define VALUE_UNSET ""
  46. /*
  47. * *_get_* functions can return an int or a char *
  48. */
  49. typedef union {
  50. int ival;
  51. char *sval;
  52. char cval;
  53. } Value;
  54. char *progname;
  55. /* Print usage information and exit */
  56. void usage(int status)
  57. {
  58. if (0 == status) {
  59. printf("Usage: %s [option...] [file...]\n", progname);
  60. printf("Report disc and track information from a CUE or TOC file.\n"
  61. "\n"
  62. "OPTIONS\n"
  63. "-h, --help print usage\n"
  64. "-i, --input-format cue|toc set format of file(s)\n"
  65. "-n, --track-number <number> only print track information for single track\n"
  66. "-d, --disc-template <template> set disc template\n"
  67. "-t, --track-template <template> set track template\n"
  68. "-V, --version print version information\n"
  69. "\n"
  70. "Default disc template: %s\n"
  71. "Default track template: %s\n"
  72. "See the manual page for more information.\n",
  73. D_TEMPLATE, T_TEMPLATE);
  74. } else {
  75. fprintf(stderr, "Try `%s --help' for more information.\n", progname);
  76. }
  77. exit (status);
  78. }
  79. /* Print version information and exit */
  80. void version()
  81. {
  82. printf("%s\n", PACKAGE_STRING);
  83. exit(0);
  84. }
  85. /*
  86. * TODO: Shouldn't we be using vprintf() to help us out with this stuff?
  87. * (Branden Robinson)
  88. */
  89. void disc_field(char *conv, int length, Cd *cd, Value *value)
  90. {
  91. char *c; /* pointer to conversion character */
  92. Cdtext *cdtext = NULL;
  93. cdtext = cd_get_cdtext(cd);
  94. c = conv + length - 1;
  95. /*
  96. * After setting value, set *c to specify value type:
  97. * 'd' integer
  98. * 's' string
  99. * 'c' character
  100. */
  101. switch (*c) {
  102. case 'A':
  103. value->sval = cdtext_get(PTI_ARRANGER, cdtext);
  104. *c = 's';
  105. break;
  106. case 'C':
  107. value->sval = cdtext_get(PTI_COMPOSER, cdtext);
  108. *c = 's';
  109. break;
  110. case 'G':
  111. value->sval = cdtext_get(PTI_GENRE, cdtext);
  112. *c = 's';
  113. break;
  114. case 'M':
  115. value->sval = cdtext_get(PTI_MESSAGE, cdtext);
  116. *c = 's';
  117. break;
  118. case 'N':
  119. value->ival = cd_get_ntrack(cd);
  120. *c = 'd';
  121. break;
  122. case 'P':
  123. value->sval = cdtext_get(PTI_PERFORMER, cdtext);
  124. *c = 's';
  125. break;
  126. case 'R':
  127. value->sval = cdtext_get(PTI_ARRANGER, cdtext);
  128. *c = 's';
  129. break;
  130. case 'S':
  131. value->sval = cdtext_get(PTI_SONGWRITER, cdtext);
  132. *c = 's';
  133. break;
  134. case 'T':
  135. value->sval = cdtext_get(PTI_TITLE, cdtext);
  136. *c = 's';
  137. break;
  138. case 'U':
  139. value->sval = cdtext_get(PTI_UPC_ISRC, cdtext);
  140. *c = 's';
  141. break;
  142. default:
  143. value->cval = *c;
  144. *c = 'c';
  145. break;
  146. }
  147. }
  148. void track_field(char *conv, int length, Cd *cd, int trackno, Value *value)
  149. {
  150. char *c; /* pointer to conversion character */
  151. Track *track = NULL;
  152. Cdtext *cdtext = NULL;
  153. track = cd_get_track(cd, trackno);
  154. cdtext = track_get_cdtext(track);
  155. c = conv + length - 1;
  156. switch (*c) {
  157. case 'a':
  158. value->sval = cdtext_get(PTI_ARRANGER, cdtext);
  159. *c = 's';
  160. break;
  161. case 'c':
  162. value->sval = cdtext_get(PTI_COMPOSER, cdtext);
  163. *c = 's';
  164. break;
  165. case 'f':
  166. value->sval = track_get_filename(track);
  167. *c = 's';
  168. break;
  169. case 'g':
  170. value->sval = cdtext_get(PTI_GENRE, cdtext);
  171. *c = 's';
  172. break;
  173. case 'i':
  174. value->sval = track_get_isrc(track);
  175. *c = 's';
  176. break;
  177. case 'm':
  178. value->sval = cdtext_get(PTI_MESSAGE, cdtext);
  179. *c = 's';
  180. break;
  181. case 'n':
  182. value->ival = trackno;
  183. *c = 'd';
  184. break;
  185. case 'p':
  186. value->sval = cdtext_get(PTI_PERFORMER, cdtext);
  187. *c = 's';
  188. break;
  189. case 's':
  190. value->sval = cdtext_get(PTI_SONGWRITER, cdtext);
  191. *c = 's';
  192. break;
  193. case 't':
  194. value->sval = cdtext_get(PTI_TITLE, cdtext);
  195. *c = 's';
  196. break;
  197. case 'u':
  198. value->sval = cdtext_get(PTI_UPC_ISRC, cdtext);
  199. *c = 's';
  200. break;
  201. default:
  202. disc_field(conv, length, cd, value);
  203. break;
  204. }
  205. }
  206. /*
  207. * Print a conversion specification.
  208. * [flag(s)][width][.precision]<conversion-char>
  209. */
  210. void print_conv(char *start, int length, Cd *cd, int trackno)
  211. {
  212. char *conv; /* copy of conversion specification */
  213. Value value;
  214. char *c; /* pointer to conversion-char */
  215. /* TODO: use strndup? */
  216. conv = malloc((unsigned) (length + 1));
  217. strncpy(conv, start, length);
  218. conv[length] = '\0';
  219. /* conversion character */
  220. if (0 == trackno) {
  221. disc_field(conv, length, cd, &value);
  222. } else {
  223. track_field(conv, length, cd, trackno, &value);
  224. }
  225. c = conv + length - 1;
  226. switch (*c) {
  227. case 'c':
  228. printf(conv, value.cval);
  229. break;
  230. case 'd':
  231. printf(conv, value.ival);
  232. break;
  233. case 's':
  234. if (NULL == value.sval)
  235. printf(conv, VALUE_UNSET);
  236. else
  237. printf(conv, value.sval);
  238. break;
  239. default:
  240. printf("%zu: ", strlen(conv));
  241. printf("%s", conv);
  242. }
  243. free(conv);
  244. }
  245. void cd_printf(char *format, Cd *cd, int trackno)
  246. {
  247. char *c; /* pointer into format */
  248. char *conv_start;
  249. int conv_length;
  250. for (c = format; '\0' != *c; c++) {
  251. if ('%' == *c) {
  252. conv_start = c;
  253. conv_length = 1;
  254. c++;
  255. /* flags */
  256. while ( \
  257. '-' == *c \
  258. || '+' == *c \
  259. || ' ' == *c \
  260. || '0' == *c \
  261. || '#' == *c \
  262. ) {
  263. conv_length++;
  264. c++;
  265. }
  266. /* field width */
  267. /* '*' not recognized */
  268. while (0 != isdigit(*c)) {
  269. conv_length++;
  270. c++;
  271. }
  272. /* precision */
  273. /* '*' not recognized */
  274. if ('.' == *c) {
  275. conv_length++;
  276. c++;
  277. while (0 != isdigit(*c)) {
  278. conv_length++;
  279. c++;
  280. }
  281. }
  282. /* length modifier (h, l, or L) */
  283. /* not recognized */
  284. /* conversion character */
  285. conv_length++;
  286. print_conv(conv_start, conv_length, cd, trackno);
  287. } else {
  288. putchar(*c);
  289. }
  290. }
  291. }
  292. int info(char *name, int format, int trackno, char *d_template, char *t_template)
  293. {
  294. Cd *cd = NULL;
  295. int ntrack;
  296. if (NULL == (cd = cf_parse(name, &format))) {
  297. fprintf(stderr, "%s: error: unable to parse input file"
  298. " `%s'\n", progname, name);
  299. return -1;
  300. }
  301. ntrack = cd_get_ntrack(cd);
  302. if (-1 == trackno) {
  303. cd_printf(d_template, cd, 0);
  304. for (trackno = 1; trackno <= ntrack; trackno++) {
  305. cd_printf(t_template, cd, trackno);
  306. }
  307. } else if (0 == trackno) {
  308. cd_printf(d_template, cd, trackno);
  309. } else if (0 < trackno && ntrack >= trackno) {
  310. cd_printf(t_template, cd, trackno);
  311. } else {
  312. fprintf(stderr, "%s: error: track number out of range\n", progname);
  313. return -1;
  314. }
  315. return 0;
  316. }
  317. /*
  318. * Translate escape sequences in a string.
  319. * The string is overwritten and terminated.
  320. * TODO: this does not handle octal and hexidecimal escapes
  321. * except for \0
  322. */
  323. void translate_escapes(char *s)
  324. {
  325. char *read;
  326. char *write;
  327. read = s;
  328. write = s;
  329. while ('\0' != *read) {
  330. if ('\\' == *read) {
  331. read++;
  332. switch (*read) {
  333. case 'a':
  334. *write = '\a';
  335. break;
  336. case 'b':
  337. *write = '\b';
  338. break;
  339. case 'f':
  340. *write = '\f';
  341. break;
  342. case 'n':
  343. *write = '\n';
  344. break;
  345. case 'r':
  346. *write = '\r';
  347. break;
  348. case 't':
  349. *write = '\t';
  350. break;
  351. case 'v':
  352. *write = '\v';
  353. break;
  354. case '0':
  355. *write = '\0';
  356. break;
  357. default:
  358. /* ?, ', " are handled by the default */
  359. *write = *read;
  360. break;
  361. }
  362. } else {
  363. *write = *read;
  364. }
  365. read++;
  366. write++;
  367. }
  368. *write = '\0';
  369. }
  370. int main(int argc, char *argv[])
  371. {
  372. int format = UNKNOWN;
  373. int trackno = -1; /* track number (-1 = unspecified,
  374. 0 = disc info) */
  375. char *d_template = NULL; /* disc template */
  376. char *t_template = NULL; /* track template */
  377. int ret = 0; /* return value of info() */
  378. /* option variables */
  379. int c;
  380. /* getopt_long() variables */
  381. extern char *optarg;
  382. extern int optind;
  383. static struct option longopts[] = {
  384. {"help", no_argument, NULL, 'h'},
  385. {"input-format", required_argument, NULL, 'i'},
  386. {"track-number", required_argument, NULL, 'n'},
  387. {"disc-template", required_argument, NULL, 'd'},
  388. {"track-template", required_argument, NULL, 't'},
  389. {"version", no_argument, NULL, 'V'},
  390. {NULL, 0, NULL, 0}
  391. };
  392. progname = argv[0];
  393. while (-1 != (c = getopt_long(argc, argv, "hi:n:d:t:V", longopts, NULL))) {
  394. switch (c) {
  395. case 'h':
  396. usage(0);
  397. break;
  398. case 'i':
  399. if (0 == strcmp("cue", optarg)) {
  400. format = CUE;
  401. } else if (0 == strcmp("toc", optarg)) {
  402. format = TOC;
  403. } else {
  404. fprintf(stderr, "%s: error: unknown input file"
  405. " format `%s'\n", progname, optarg);
  406. usage(1);
  407. }
  408. break;
  409. case 'n':
  410. trackno = atoi(optarg);
  411. break;
  412. case 'd':
  413. d_template = optarg;
  414. break;
  415. case 't':
  416. t_template = optarg;
  417. break;
  418. case 'V':
  419. version();
  420. break;
  421. default:
  422. usage(1);
  423. break;
  424. }
  425. }
  426. /* If no disc or track template is set, use the defaults for both. */
  427. /* TODO: alternative to strdup to get variable strings? */
  428. if (NULL == d_template && NULL == t_template) {
  429. d_template = strdup(D_TEMPLATE);
  430. t_template = strdup(T_TEMPLATE);
  431. } else {
  432. if (NULL == d_template) {
  433. d_template = strdup("");
  434. }
  435. if (NULL == t_template) {
  436. t_template = strdup("");
  437. }
  438. }
  439. /* Translate escape sequences. */
  440. translate_escapes(d_template);
  441. translate_escapes(t_template);
  442. /* What we do depends on the number of operands. */
  443. if (optind == argc) {
  444. /* No operands: report information about stdin. */
  445. ret = info("-", format, trackno, d_template, t_template);
  446. } else {
  447. /* Report information about each operand. */
  448. for (; optind < argc; optind++) {
  449. ret = info(argv[optind], format, trackno, d_template, t_template);
  450. /* Exit if info() returns nonzero. */
  451. if (!ret) {
  452. break;
  453. }
  454. }
  455. }
  456. return ret;
  457. }