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.
 
 
 
 
 
 

523 lines
9.8 KiB

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