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.
 
 
 
 
 
 

486 lines
9.1 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 <unistd.h> /* getopt() */
  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 [-h] [-i cue|toc] [-n <tracknumer>] [-d <template>] [-t <template>] [file...]\n", progname);
  54. fputs("\
  55. \n\
  56. OPTIONS\n\
  57. -h print usage\n\
  58. -i cue|toc set format of file(s)\n\
  59. -n <tracknumber> only print track information for track tracknumer\n\
  60. -d <template> set disc template (see TEMPLATE EXPANSION)\n\
  61. -t <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, "%s: syntax error\n", progname);
  93. fprintf(stderr, "run `%s -h' for usage\n", progname);
  94. }
  95. exit (status);
  96. }
  97. void disc_field (char *conv, int length, Cd *cd, Value *value)
  98. {
  99. char *c; /* pointer to conversion character */
  100. Cdtext *cdtext = NULL;
  101. cdtext = cd_get_cdtext(cd);
  102. c = conv + length - 1;
  103. /*
  104. * after setting value, set *c to specify value type
  105. * 'd' integer
  106. * 's' string
  107. * 'c' character
  108. */
  109. switch (*c) {
  110. case 'A':
  111. value->sval = cdtext_get(PTI_ARRANGER, cdtext);
  112. *c = 's';
  113. break;
  114. case 'C':
  115. value->sval = cdtext_get(PTI_COMPOSER, cdtext);
  116. *c = 's';
  117. break;
  118. case 'G':
  119. value->sval = cdtext_get(PTI_GENRE, cdtext);
  120. *c = 's';
  121. break;
  122. case 'M':
  123. value->sval = cdtext_get(PTI_MESSAGE, cdtext);
  124. *c = 's';
  125. break;
  126. case 'N':
  127. value->ival = cd_get_ntrack(cd);
  128. *c = 'd';
  129. break;
  130. case 'P':
  131. value->sval = cdtext_get(PTI_PERFORMER, cdtext);
  132. *c = 's';
  133. break;
  134. case 'R':
  135. value->sval = cdtext_get(PTI_ARRANGER, cdtext);
  136. *c = 's';
  137. break;
  138. case 'S':
  139. value->sval = cdtext_get(PTI_SONGWRITER, cdtext);
  140. *c = 's';
  141. break;
  142. case 'T':
  143. value->sval = cdtext_get(PTI_TITLE, cdtext);
  144. *c = 's';
  145. break;
  146. case 'U':
  147. value->sval = cdtext_get(PTI_UPC_ISRC, cdtext);
  148. *c = 's';
  149. break;
  150. default:
  151. value->cval = *c;
  152. *c = 'c';
  153. break;
  154. }
  155. }
  156. void track_field (char *conv, int length, Cd *cd, int trackno, Value *value)
  157. {
  158. char *c; /* pointer to conversion character */
  159. Track *track = NULL;
  160. Cdtext *cdtext = NULL;
  161. track = cd_get_track(cd, trackno);
  162. cdtext = track_get_cdtext(track);
  163. c = conv + length - 1;
  164. switch (*c) {
  165. case 'a':
  166. value->sval = cdtext_get(PTI_ARRANGER, cdtext);
  167. *c = 's';
  168. break;
  169. case 'c':
  170. value->sval = cdtext_get(PTI_COMPOSER, cdtext);
  171. *c = 's';
  172. break;
  173. case 'f':
  174. value->sval = track_get_filename(track);
  175. *c = 's';
  176. break;
  177. case 'g':
  178. value->sval = cdtext_get(PTI_GENRE, cdtext);
  179. *c = 's';
  180. break;
  181. case 'i':
  182. value->sval = track_get_isrc(track);
  183. *c = 's';
  184. break;
  185. case 'm':
  186. value->sval = cdtext_get(PTI_MESSAGE, cdtext);
  187. *c = 's';
  188. break;
  189. case 'n':
  190. value->ival = trackno;
  191. *c = 'd';
  192. break;
  193. case 'p':
  194. value->sval = cdtext_get(PTI_PERFORMER, cdtext);
  195. *c = 's';
  196. break;
  197. case 's':
  198. value->sval = cdtext_get(PTI_SONGWRITER, cdtext);
  199. *c = 's';
  200. break;
  201. case 't':
  202. value->sval = cdtext_get(PTI_TITLE, cdtext);
  203. *c = 's';
  204. break;
  205. case 'u':
  206. value->sval = cdtext_get(PTI_UPC_ISRC, cdtext);
  207. *c = 's';
  208. break;
  209. default:
  210. disc_field(conv, length, cd, value);
  211. break;
  212. }
  213. }
  214. /* print a % conversion specification
  215. * %[flag(s)][width][.precision]<conversion-char>
  216. */
  217. void print_conv (char *start, int length, Cd *cd, int trackno)
  218. {
  219. char *conv; /* copy of conversion specification */
  220. Value value;
  221. char *c; /* pointer to conversion-char */
  222. /* TODO: use strndup? */
  223. conv = malloc ((unsigned) (length + 1));
  224. strncpy(conv, start, length);
  225. conv[length] = '\0';
  226. /* conversion character */
  227. if (0 == trackno)
  228. disc_field(conv, length, cd, &value);
  229. else
  230. track_field(conv, length, cd, trackno, &value);
  231. c = conv + length - 1;
  232. switch (*c) {
  233. case 'c':
  234. printf(conv, value.cval);
  235. break;
  236. case 'd':
  237. printf(conv, value.ival);
  238. break;
  239. case 's':
  240. if (NULL == value.sval)
  241. printf(conv, VALUE_UNSET);
  242. else
  243. printf(conv, value.sval);
  244. break;
  245. default:
  246. printf("%d: ", strlen(conv));
  247. printf("%s", conv);
  248. }
  249. free(conv);
  250. }
  251. void cd_printf (char *format, Cd *cd, int trackno)
  252. {
  253. char *c; /* pointer into format */
  254. char *conv_start;
  255. int conv_length;
  256. for (c = format; '\0' != *c; c++) {
  257. if ('%' == *c) {
  258. conv_start = c;
  259. conv_length = 1;
  260. c++;
  261. /* flags */
  262. while ( \
  263. '-' == *c \
  264. || '+' == *c \
  265. || ' ' == *c \
  266. || '0' == *c \
  267. || '#' == *c \
  268. ) {
  269. conv_length++;
  270. c++;
  271. }
  272. /* field width */
  273. /* '*' not recognized */
  274. while (0 != isdigit(*c)) {
  275. conv_length++;
  276. c++;
  277. }
  278. /* precision */
  279. /* '*' not recognized */
  280. if ('.' == *c) {
  281. conv_length++;
  282. c++;
  283. while (0 != isdigit(*c)) {
  284. conv_length++;
  285. c++;
  286. }
  287. }
  288. /* length modifier (h, l, or L) */
  289. /* not recognized */
  290. /* conversion character */
  291. conv_length++;
  292. print_conv(conv_start, conv_length, cd, trackno);
  293. } else {
  294. putchar(*c);
  295. }
  296. }
  297. }
  298. int info (char *name, int format, int trackno, char *d_template, char *t_template)
  299. {
  300. Cd *cd = NULL;
  301. int ntrack;
  302. if (NULL == (cd = cf_parse(name, &format))) {
  303. fprintf(stderr, "%s: input file error\n", name);
  304. return -1;
  305. }
  306. ntrack = cd_get_ntrack(cd);
  307. if (-1 == trackno) {
  308. cd_printf(d_template, cd, 0);
  309. for (trackno = 1; trackno <= ntrack; trackno++) {
  310. cd_printf(t_template, cd, trackno);
  311. }
  312. } else if (0 == trackno) {
  313. cd_printf(d_template, cd, trackno);
  314. } else if (0 < trackno && ntrack >= trackno) {
  315. cd_printf(t_template, cd, trackno);
  316. } else {
  317. fprintf(stderr, "%s: track number out of range\n", progname);
  318. return -1;
  319. }
  320. return 0;
  321. }
  322. /* translate escape sequences in a string
  323. * string is overwritten and terminated
  324. * TODO: this does not handle octal and hexidecimal escapes
  325. * except for \0
  326. */
  327. void translate_escapes(char *s)
  328. {
  329. char *read;
  330. char *write;
  331. read = s;
  332. write = s;
  333. while ('\0' != *read) {
  334. if ('\\' == *read) {
  335. read++;
  336. switch (*read) {
  337. case 'a':
  338. *write = '\a';
  339. break;
  340. case 'b':
  341. *write = '\b';
  342. break;
  343. case 'f':
  344. *write = '\f';
  345. break;
  346. case 'n':
  347. *write = '\n';
  348. break;
  349. case 'r':
  350. *write = '\r';
  351. break;
  352. case 't':
  353. *write = '\t';
  354. break;
  355. case 'v':
  356. *write = '\v';
  357. break;
  358. case '0':
  359. *write = '\0';
  360. break;
  361. default:
  362. /* ?, ', " are handled by the default */
  363. *write = *read;
  364. break;
  365. }
  366. } else {
  367. *write = *read;
  368. }
  369. read++;
  370. write++;
  371. }
  372. *write = '\0';
  373. }
  374. int main (int argc, char **argv)
  375. {
  376. int format = UNKNOWN;
  377. int trackno = -1; /* track number (-1 = unspecified, 0 = disc info) */
  378. char *d_template = NULL; /* disc template */
  379. char *t_template = NULL; /* track template */
  380. /* getopt () variables */
  381. char c;
  382. extern char *optarg;
  383. extern int optind;
  384. progname = *argv;
  385. while (-1 != (c = getopt(argc, argv, "hi:n:d:t:"))) {
  386. switch (c) {
  387. case 'h':
  388. usage(0);
  389. break;
  390. case 'i':
  391. if (0 == strcmp("cue", optarg))
  392. format = CUE;
  393. else if (0 == strcmp("toc", optarg))
  394. format = TOC;
  395. break;
  396. case 'n':
  397. trackno = atoi(optarg);
  398. break;
  399. case 'd':
  400. d_template = optarg;
  401. break;
  402. case 't':
  403. t_template = optarg;
  404. break;
  405. default:
  406. usage(1);
  407. break;
  408. }
  409. }
  410. /* if no disc or track template is set, use the defaults for both */
  411. /* TODO: alternative to strdup to get variable strings? */
  412. if (NULL == d_template && NULL == t_template) {
  413. d_template = strdup(D_TEMPLATE);
  414. t_template = strdup(T_TEMPLATE);
  415. } else {
  416. if (NULL == d_template)
  417. d_template = strdup("");
  418. if (NULL == t_template)
  419. t_template = strdup("");
  420. }
  421. /* translate escape sequences */
  422. translate_escapes(d_template);
  423. translate_escapes(t_template);
  424. if (optind == argc) {
  425. info("-", format, trackno, d_template, t_template);
  426. } else {
  427. for (; optind < argc; optind++)
  428. info(argv[optind], format, trackno, d_template, t_template);
  429. }
  430. return 0;
  431. }