25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

469 lines
8.7 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 TRACKNUMBER] [-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 TRACKNUMBER\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. ", stdout);
  88. fprintf(stdout, "default disc template is:\n%s\n", D_TEMPLATE);
  89. fprintf(stdout, "default track template is:\n%s\n", T_TEMPLATE);
  90. } else {
  91. fprintf(stderr, "%s: syntax error\n", progname);
  92. fprintf(stderr, "run `%s -h' 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. /* print an single-character escape
  251. * `c' is the character after the `/'
  252. * NOTE: this does not handle octal and hexidecimal escapes
  253. * except for \0
  254. */
  255. void print_esc (char *c)
  256. {
  257. switch (*c) {
  258. case 'a':
  259. putchar('\a');
  260. break;
  261. case 'b':
  262. putchar('\b');
  263. break;
  264. case 'f':
  265. putchar('\f');
  266. break;
  267. case 'n':
  268. putchar('\n');
  269. break;
  270. case 'r':
  271. putchar('\r');
  272. break;
  273. case 't':
  274. putchar('\t');
  275. break;
  276. case 'v':
  277. putchar('\v');
  278. break;
  279. case '0':
  280. putchar('\0');
  281. break;
  282. default:
  283. /* ?, ', " are handled by the default */
  284. putchar(*c);
  285. break;
  286. }
  287. }
  288. void cd_printf (char *format, Cd *cd, int trackno)
  289. {
  290. char *c; /* pointer into format */
  291. char *conv_start;
  292. int conv_length;
  293. for (c = format; '\0' != *c; c++) {
  294. switch (*c) {
  295. case '%':
  296. conv_start = c;
  297. conv_length = 1;
  298. c++;
  299. /* flags */
  300. while ( \
  301. '-' == *c \
  302. || '+' == *c \
  303. || ' ' == *c \
  304. || '0' == *c \
  305. || '#' == *c \
  306. ) {
  307. conv_length++;
  308. c++;
  309. }
  310. /* field width */
  311. /* '*' not recognized */
  312. while (0 != isdigit(*c)) {
  313. conv_length++;
  314. c++;
  315. }
  316. /* precision */
  317. /* '*' not recognized */
  318. if ('.' == *c) {
  319. conv_length++;
  320. c++;
  321. while (0 != isdigit(*c)) {
  322. conv_length++;
  323. c++;
  324. }
  325. }
  326. /* length modifier (h, l, or L) */
  327. /* not recognized */
  328. /* conversion character */
  329. conv_length++;
  330. print_conv(conv_start, conv_length, cd, trackno);
  331. break;
  332. case '\\':
  333. c++;
  334. print_esc(c);
  335. break;
  336. default:
  337. putchar(*c);
  338. break;
  339. }
  340. }
  341. }
  342. int info (char *name, int format, int trackno, char *d_template, char *t_template)
  343. {
  344. Cd *cd = NULL;
  345. int ntrack;
  346. if (NULL == (cd = cf_parse(name, &format))) {
  347. fprintf(stderr, "%s: input file error\n", name);
  348. return -1;
  349. }
  350. ntrack = cd_get_ntrack(cd);
  351. if (-1 == trackno) {
  352. cd_printf(d_template, cd, 0);
  353. for (trackno = 1; trackno <= ntrack; trackno++) {
  354. cd_printf(t_template, cd, trackno);
  355. }
  356. } else if (0 == trackno) {
  357. cd_printf(d_template, cd, trackno);
  358. } else if (0 < trackno && ntrack >= trackno) {
  359. cd_printf(t_template, cd, trackno);
  360. } else {
  361. fprintf(stderr, "%s: track number out of range\n", progname);
  362. return -1;
  363. }
  364. return 0;
  365. }
  366. int main (int argc, char **argv)
  367. {
  368. int format = UNKNOWN;
  369. int trackno = -1; /* track number (-1 = unspecified, 0 = disc info) */
  370. char *d_template = NULL; /* disc template */
  371. char *t_template = NULL; /* track template */
  372. /* getopt () variables */
  373. char c;
  374. extern char *optarg;
  375. extern int optind;
  376. progname = *argv;
  377. while (-1 != (c = getopt(argc, argv, "hi:n:d:t:"))) {
  378. switch (c) {
  379. case 'h':
  380. usage(0);
  381. break;
  382. case 'i':
  383. if (0 == strcmp("cue", optarg))
  384. format = CUE;
  385. else if (0 == strcmp("toc", optarg))
  386. format = TOC;
  387. break;
  388. case 'n':
  389. trackno = atoi(optarg);
  390. break;
  391. case 'd':
  392. d_template = optarg;
  393. break;
  394. case 't':
  395. t_template = optarg;
  396. break;
  397. default:
  398. usage(1);
  399. break;
  400. }
  401. }
  402. /* if no disc or track template is set, use the defaults for both */
  403. if (NULL == d_template && NULL == t_template) {
  404. d_template = D_TEMPLATE;
  405. t_template = T_TEMPLATE;
  406. } else {
  407. if (NULL == d_template)
  408. d_template = "";
  409. if (NULL == t_template)
  410. t_template = "";
  411. }
  412. if (optind == argc) {
  413. info("-", format, trackno, d_template, t_template);
  414. } else {
  415. for (; optind < argc; optind++)
  416. info(argv[optind], format, trackno, d_template, t_template);
  417. }
  418. return 0;
  419. }