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.
 
 
 
 
 
 

497 lines
9.5 KiB

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