~ubuntu-branches/ubuntu/utopic/cdrdao/utopic

« back to all changes in this revision

Viewing changes to utils/cue2toc.c

  • Committer: Bazaar Package Importer
  • Author(s): Andrew Suffield
  • Date: 2004-06-24 22:33:16 UTC
  • Revision ID: james.westby@ubuntu.com-20040624223316-534onzugaeeyq61j
Tags: upstream-1.1.9
ImportĀ upstreamĀ versionĀ 1.1.9

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* cue2toc.c - conversion routines
 
2
 * Copyright (C) 2004 Matthias Czapla <dermatsch@gmx.de>
 
3
 *
 
4
 * This file is part of cue2toc.
 
5
 *
 
6
 * This program is free software; you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation; either version 2 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * This program is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with this program; if not, write to the Free Software
 
18
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 
19
 */
 
20
 
 
21
#include <config.h>
 
22
#include <stdio.h>
 
23
#include <stdlib.h>
 
24
#include <string.h>
 
25
#include <ctype.h>
 
26
#include <unistd.h>
 
27
#include <errno.h>
 
28
#include <stdarg.h>
 
29
#include "cue2toc.h"
 
30
#include "timecode.h"
 
31
 
 
32
#define TCBUFLEN 9      /* Buffer length for timecode strings (HH:MM:SS) */
 
33
#define MAXCMDLEN 10    /* Longest command (currently SONGWRITER) */
 
34
 
 
35
extern const char *progname;    /* Set to argv[0] by main */
 
36
extern int verbose;             /* Set by main */
 
37
 
 
38
/*
 
39
 * Input is divied into tokens that are separated by whitespace, horizantal
 
40
 * tabulator, line feed and carriage return. Tokens can be either commands
 
41
 * from a fixed set or strings. If a string is to contain any of the token
 
42
 * delimiting characters it must be enclosed in double quotes.
 
43
 */
 
44
 
 
45
static const char token_delimiter[] = { ' ', '\t', '\n', '\r' };
 
46
 
 
47
/* Return true if c is one of token_delimiter */
 
48
static int
 
49
isdelim(int c)
 
50
{
 
51
        int i;
 
52
        int n = sizeof(token_delimiter);
 
53
 
 
54
        for (i = 0; i < n; i++)
 
55
                if (c == token_delimiter[i])
 
56
                        return 1;
 
57
        return 0;
 
58
}
 
59
 
 
60
/* Used as return type for get_command and index into cmds */
 
61
enum command { REM, CATALOG, CDTEXTFILE,
 
62
        FILECMD, PERFORMER, SONGWRITER, TITLE, TRACK, FLAGS, DCP,
 
63
        FOURCH, PRE, SCMS, ISRC, PREGAP, INDEX, POSTGAP, BINARY,
 
64
        MOTOROLA, AIFF, WAVE, MP3, UNKNOWN, END };
 
65
 
 
66
/* Except the last two these are the valid CUE commands */
 
67
char cmds[][MAXCMDLEN + 1] = { "REM", "CATALOG", "CDTEXTFILE",
 
68
        "FILE", "PERFORMER", "SONGWRITER", "TITLE", "TRACK", "FLAGS", "DCP",
 
69
        "4CH", "PRE", "SCMS", "ISRC", "PREGAP", "INDEX", "POSTGAP", "BINARY",
 
70
        "MOTOROLA", "AIFF", "WAVE", "MP3", "UNKNOWN", "END" };
 
71
 
 
72
/* These are for error messages */
 
73
static const char *fname = "stdin";
 
74
static long line;               /* current line number */
 
75
static long tokenstart;         /* line where last token started */
 
76
 
 
77
/* To generate meaningful error messages in check_once */
 
78
enum scope { CUESHEET, GLOBAL, ONETRACK };
 
79
 
 
80
/* Fatal error while processing input file */
 
81
static void
 
82
err_fail(const char *s)
 
83
{
 
84
        fprintf(stderr, "%s:%s:%ld: %s\n", progname, fname, tokenstart, s);
 
85
        exit(EXIT_FAILURE);
 
86
}
 
87
 
 
88
/* Fatal error */
 
89
static void
 
90
err_fail2(const char *s)
 
91
{
 
92
        fprintf(stderr, "%s: %s\n", progname, s);
 
93
        exit(EXIT_FAILURE);
 
94
}
 
95
 
 
96
/* EOF while expecting more */
 
97
static void
 
98
err_earlyend()
 
99
{
 
100
        fprintf(stderr, "%s:%s:%ld: Premature end of file\n", progname,
 
101
                fname, line);
 
102
        exit(EXIT_FAILURE);
 
103
}
 
104
 
 
105
/* Warning. Keep on going. */
 
106
static void
 
107
err_warn(const char *s)
 
108
{
 
109
        if (!verbose)
 
110
                return;
 
111
        fprintf(stderr, "%s:%s:%ld: Warning, %s\n", progname, fname,
 
112
                tokenstart, s);
 
113
}
 
114
 
 
115
/* Get next command from file */
 
116
static enum command
 
117
get_command(FILE *f)
 
118
{
 
119
        int c;
 
120
        char buf[MAXCMDLEN + 1];
 
121
        int i = 0;
 
122
 
 
123
        /* eat whitespace */
 
124
        do {
 
125
                c = getc(f);
 
126
                if (c == '\n')
 
127
                        line++;
 
128
        } while (isdelim(c));
 
129
 
 
130
        if (c == EOF)
 
131
                return END;
 
132
        
 
133
        tokenstart = line;
 
134
 
 
135
        /* get command, transform to upper case */
 
136
        do {
 
137
                buf[i++] = toupper(c);
 
138
                c = getc(f);
 
139
        } while (!isdelim(c) && c!= EOF && i < MAXCMDLEN);
 
140
 
 
141
        if (!isdelim(c)) return UNKNOWN; /* command longer than MAXCMDLEN */
 
142
        if (c == EOF) return END;
 
143
        if (c == '\n') line++;
 
144
 
 
145
        buf[i] = '\0';
 
146
 
 
147
        if (strcmp(buf, cmds[REM]) == 0) return REM;
 
148
        else if (strcmp(buf, cmds[CATALOG]) == 0) return CATALOG;
 
149
        else if (strcmp(buf, cmds[CDTEXTFILE]) == 0) return CDTEXTFILE;
 
150
        else if (strcmp(buf, cmds[FILECMD]) == 0) return FILECMD;
 
151
        else if (strcmp(buf, cmds[PERFORMER]) == 0) return PERFORMER;
 
152
        else if (strcmp(buf, cmds[SONGWRITER]) == 0) return SONGWRITER;
 
153
        else if (strcmp(buf, cmds[TITLE]) == 0) return TITLE;
 
154
        else if (strcmp(buf, cmds[TRACK]) == 0) return TRACK;
 
155
        else if (strcmp(buf, cmds[FLAGS]) == 0) return FLAGS;
 
156
        else if (strcmp(buf, cmds[DCP]) == 0) return DCP;
 
157
        else if (strcmp(buf, cmds[FOURCH]) == 0) return FOURCH;
 
158
        else if (strcmp(buf, cmds[PRE]) == 0) return PRE;
 
159
        else if (strcmp(buf, cmds[SCMS]) == 0) return SCMS;
 
160
        else if (strcmp(buf, cmds[ISRC]) == 0) return ISRC;
 
161
        else if (strcmp(buf, cmds[PREGAP]) == 0) return PREGAP;
 
162
        else if (strcmp(buf, cmds[INDEX]) == 0) return INDEX;
 
163
        else if (strcmp(buf, cmds[POSTGAP]) == 0) return POSTGAP;
 
164
        else if (strcmp(buf, cmds[BINARY]) == 0) return BINARY;
 
165
        else if (strcmp(buf, cmds[MOTOROLA]) == 0) return MOTOROLA;
 
166
        else if (strcmp(buf, cmds[AIFF]) == 0) return AIFF;
 
167
        else if (strcmp(buf, cmds[WAVE]) == 0) return WAVE;
 
168
        else if (strcmp(buf, cmds[MP3]) == 0) return MP3;
 
169
        else return UNKNOWN;
 
170
}
 
171
 
 
172
/* Skip leading token delimiters then read at most n chars from f into s.
 
173
 * Put terminating Null at the end of s. This implies that s must be
 
174
 * really n + 1. Return number of characters written to s. The only case to
 
175
 * return zero is on EOF before any character was read.
 
176
 * Exit the program indicating failure if string is longer than n. */
 
177
static size_t
 
178
get_string(FILE *f, char *s, size_t n)
 
179
{
 
180
        int c;
 
181
        size_t i = 0;
 
182
 
 
183
        /* eat whitespace */
 
184
        do {
 
185
                c = getc(f);
 
186
                if (c == '\n')
 
187
                        line++;
 
188
        } while (isdelim(c));
 
189
 
 
190
        if (c == EOF)
 
191
                return 0;
 
192
 
 
193
        tokenstart = line;
 
194
 
 
195
        if (c == '\"') {
 
196
                c = getc(f);
 
197
                if (c == '\n') line++;
 
198
                while (c != '\"' && c != EOF && i < n) {
 
199
                        s[i++] = c;
 
200
                        c = getc(f);
 
201
                        if (c == '\n') line++;
 
202
                }
 
203
                if (i == n && c != '\"' && c != EOF)
 
204
                        err_fail("String too long");
 
205
        } else {
 
206
                while (!isdelim(c) && c != EOF && i < n) {
 
207
                        s[i++] = c;
 
208
                        c = getc(f);
 
209
                }
 
210
                if (i == n && !isdelim(c) && c != EOF)
 
211
                        err_fail("String too long");
 
212
        }
 
213
        if (i == 0) err_fail("Empty string");
 
214
        if (c == '\n') line++;
 
215
        s[i] = '\0';
 
216
 
 
217
        return i;
 
218
}
 
219
 
 
220
/* Return track mode */
 
221
static enum track_mode
 
222
get_track_mode(FILE *f)
 
223
{
 
224
        char buf[] = "MODE1/2048";
 
225
        char *pbuf = buf;
 
226
 
 
227
        if (get_string(f, buf, sizeof(buf) - 1) < 1)
 
228
                err_fail("Illegal track mode");
 
229
 
 
230
        /* transform to upper case */
 
231
        while (*pbuf) {
 
232
                *pbuf = toupper(*pbuf);
 
233
                pbuf++;
 
234
        }
 
235
 
 
236
        if (strcmp(buf, "AUDIO") == 0) return AUDIO;
 
237
        else if (strcmp(buf, "MODE1/2048") == 0) return MODE1;
 
238
        else if (strcmp(buf, "MODE1/2352") == 0) return MODE1_RAW;
 
239
        else if (strcmp(buf, "MODE2/2336") == 0) return MODE2;
 
240
        else if (strcmp(buf, "MODE2/2352") == 0) return MODE2_RAW;
 
241
        else err_fail("Unsupported track mode");
 
242
}
 
243
 
 
244
static void check_once(enum command cmd, char *s, enum scope sc);
 
245
 
 
246
/* Read at most CDTEXTLEN chars into s */
 
247
static void
 
248
get_cdtext(FILE *f, enum command cmd, char *s, enum scope sc)
 
249
{
 
250
        check_once(cmd, s, sc);
 
251
        if (get_string(f, s, CDTEXTLEN) < 1)
 
252
                err_earlyend();
 
253
}
 
254
 
 
255
/* All strings have their first character initialized to '\0' so if s[0]
 
256
   is not Null the cmd has already been seen in input. In this case print
 
257
   a message end exit program indicating failure. The only purpose of the
 
258
   arguments cmd and sc is to print meaningful error messages. */
 
259
static void
 
260
check_once(enum command cmd, char *s, enum scope sc)
 
261
{
 
262
        if (s[0] == '\0')
 
263
                return;
 
264
        fprintf(stderr, "%s:%s:%ld: %s allowed only once", progname, fname,
 
265
                line, cmds[cmd]);
 
266
        switch (sc) {
 
267
        case CUESHEET:  fprintf(stderr, "\n"); break;
 
268
        case GLOBAL:    fprintf(stderr, " in global section\n"); break;
 
269
        case ONETRACK:  fprintf(stderr, " per track\n"); break;
 
270
        }
 
271
        exit(EXIT_FAILURE);
 
272
}
 
273
 
 
274
/* If this is a data track and does not start at position zero exit the
 
275
   program. The TOC format has no way to specify a data track using only a
 
276
   portion past the first byte of a binary file. */
 
277
static void
 
278
check_cutting_binary(struct trackspec *tr)
 
279
{
 
280
        if (tr->mode == AUDIO)
 
281
                return;
 
282
        if (tr->pregap_data_from_file) {
 
283
                if (tr->pregap < tr->start)
 
284
                        err_fail("TOC format does not allow cutting binary "
 
285
                                 "files. Try burning CUE file directly.\n");
 
286
        } else
 
287
                if (tr->start > 0)
 
288
                        err_fail("TOC format does not allow cutting binary "
 
289
                                 "files. Try burning CUE file directly.\n");
 
290
}
 
291
 
 
292
/* Allocate, initialize and return new track */
 
293
static struct trackspec*
 
294
new_track(void)
 
295
{
 
296
        struct trackspec *track;
 
297
        int i;
 
298
 
 
299
        if ((track = (struct trackspec*) malloc(sizeof(struct trackspec)))
 
300
            == NULL)
 
301
                err_fail("Memory allocation error in new_track()");
 
302
 
 
303
        track->copy = track->pre_emphasis = track->four_channel_audio 
 
304
          = track->pregap_data_from_file = 0;
 
305
        track->isrc[0] = track->title[0] = track->performer[0]
 
306
          = track->songwriter[0] = track->filename[0] = '\0';
 
307
        track->pregap = track->start = track->postgap = -1;
 
308
 
 
309
        for (i = 0; i < NUM_OF_INDEXES; i++)
 
310
                track->indexes[i] = -1;
 
311
        track->next = NULL;
 
312
 
 
313
        return track;
 
314
}
 
315
 
 
316
/* Read the cuefile and return a pointer to the cuesheet */
 
317
struct cuesheet*
 
318
read_cue(const char *cuefile, const char *wavefile)
 
319
{
 
320
        FILE *f;
 
321
        enum command cmd;
 
322
        struct cuesheet *cs = NULL;
 
323
        struct trackspec *track = NULL;
 
324
        size_t n;
 
325
        int c;
 
326
        char file[FILENAMELEN + 1];
 
327
        enum command filetype = UNKNOWN;
 
328
        char timecode_buffer[TCBUFLEN];
 
329
        char devnull[FILENAMELEN + 1];  /* just for eating CDTEXTFILE arg */
 
330
 
 
331
        if (NULL == cuefile) {
 
332
                f = stdin;
 
333
        } else if (NULL == (f = fopen(cuefile, "r"))) {
 
334
                fprintf(stderr, "%s: Could not open file \"%s\" for "
 
335
                        "reading: %s\n", progname, cuefile, strerror(errno));
 
336
                exit(EXIT_FAILURE);
 
337
        }
 
338
        if (cuefile)
 
339
                fname = cuefile;
 
340
 
 
341
        if ((cs = (struct cuesheet*) malloc(sizeof(struct cuesheet))) == NULL)
 
342
                err_fail("Memory allocation error in read_cue()");
 
343
 
 
344
        cs->catalog[0] = '\0';
 
345
        cs->type = 0;
 
346
        cs->title[0] = '\0';
 
347
        cs->performer[0] = '\0';
 
348
        cs->songwriter[0] = '\0';
 
349
        cs->tracklist = NULL;
 
350
 
 
351
        file[0] = '\0';
 
352
        line = 1;
 
353
 
 
354
        /* global section */
 
355
        while ((cmd = get_command(f)) != TRACK) {
 
356
                switch (cmd) {
 
357
                case UNKNOWN:
 
358
                        err_fail("Unknown command");
 
359
                case END:
 
360
                        err_earlyend();
 
361
                case REM:
 
362
                        c = getc(f);
 
363
                        while (c != '\n' && c != EOF)
 
364
                                c = getc(f);
 
365
                        break;
 
366
                case CDTEXTFILE:
 
367
                        err_warn("ignoring CDTEXTFILE...");
 
368
                        if (get_string(f, devnull, FILENAMELEN) == 0)
 
369
                                err_warn("Syntactically incorrect "
 
370
                                         "CDTEXTFILE command. But who "
 
371
                                         "cares...");
 
372
                        break;
 
373
                case CATALOG:
 
374
                        check_once(CATALOG, cs->catalog, CUESHEET);
 
375
                        n = get_string(f, cs->catalog, 13);
 
376
                        if (n != 13)
 
377
                                err_fail("Catalog number must be 13 "
 
378
                                         "characters long");
 
379
                        break;
 
380
                case TITLE:
 
381
                        get_cdtext(f, TITLE, cs->title, GLOBAL);
 
382
                        break;
 
383
                case PERFORMER:
 
384
                        get_cdtext(f, PERFORMER, cs->performer, GLOBAL);
 
385
                        break;
 
386
                case SONGWRITER:
 
387
                        get_cdtext(f, SONGWRITER, cs->songwriter, GLOBAL);
 
388
                        break;
 
389
                case FILECMD:
 
390
                        check_once(FILECMD, file, GLOBAL);
 
391
                        if (get_string(f, file, FILENAMELEN) < 1)
 
392
                                err_earlyend();
 
393
 
 
394
                        switch (cmd = get_command(f)) {
 
395
                        case MOTOROLA:
 
396
                                err_warn("big endian binary file");
 
397
                        case BINARY:
 
398
                                filetype = BINARY; break;
 
399
                        case AIFF: case MP3:
 
400
                                err_warn("AIFF and MP3 not supported by "
 
401
                                         "cdrdao");
 
402
                        case WAVE:
 
403
                                if (wavefile) {
 
404
                                        strncpy(file, wavefile, FILENAMELEN);
 
405
                                        file[FILENAMELEN] = '\0';
 
406
                                }
 
407
                                filetype = WAVE; break;
 
408
                        default:
 
409
                                err_fail("Unsupported file type");
 
410
                        }
 
411
                        break;
 
412
                default:
 
413
                        err_fail("Command not allowed in global section");
 
414
                        break;
 
415
                }
 
416
 
 
417
        }
 
418
 
 
419
        /* leaving global section, entering track specifications */
 
420
        if (file[0] == '\0')
 
421
                err_fail("TRACK without previous FILE");
 
422
 
 
423
        while (cmd != END) {
 
424
                switch(cmd) {
 
425
                case UNKNOWN:
 
426
                        err_fail("Unknown command");
 
427
                case REM:
 
428
                        c = getc(f);
 
429
                        while (c != '\n' && c != EOF)
 
430
                                c = getc(f);
 
431
                        break;
 
432
                case TRACK:
 
433
                        if (track == NULL)      /* first track */
 
434
                                cs->tracklist = track = new_track();
 
435
                        else {
 
436
                                check_cutting_binary(track);
 
437
                                track = track->next = new_track();
 
438
                        }
 
439
 
 
440
                        /* the CUE format is "TRACK nn MODE" but we are not
 
441
                           interested in the track number */
 
442
                        while (isdelim(c = getc(f)))
 
443
                                if (c == '\n') line++;
 
444
                        while (!isdelim(c = getc(f))) ;
 
445
                        if (c == '\n') line++;
 
446
 
 
447
                        track->mode = get_track_mode(f);
 
448
 
 
449
                        /* audio tracks with binary files seem quite common */
 
450
                        /*
 
451
                        if (track->mode == AUDIO && filetype == BINARY
 
452
                            || track->mode != AUDIO && filetype == WAVE)
 
453
                                err_fail("File and track type mismatch");
 
454
                        */
 
455
 
 
456
                        strcpy(track->filename, file);
 
457
                        break;
 
458
                case TITLE:
 
459
                        get_cdtext(f, TITLE, track->title, ONETRACK);
 
460
                        break;
 
461
                case PERFORMER:
 
462
                        get_cdtext(f, PERFORMER, track->performer, ONETRACK);
 
463
                        break;
 
464
                case SONGWRITER:
 
465
                        get_cdtext(f, SONGWRITER, track->songwriter, ONETRACK);
 
466
                        break;
 
467
                case ISRC:
 
468
                        check_once(ISRC, track->isrc, ONETRACK);
 
469
                        if (get_string(f, track->isrc, 12) != 12)
 
470
                                err_fail("ISRC must be 12 characters long");
 
471
                        break;
 
472
                case FLAGS:
 
473
                        if (track->copy || track->pre_emphasis 
 
474
                            || track->four_channel_audio)
 
475
                                err_fail("FLAGS allowed only once per track");
 
476
 
 
477
                        /* get the flags */
 
478
                        cmd = get_command(f);
 
479
                        while (cmd == DCP || cmd == FOURCH || cmd == PRE
 
480
                               || cmd == SCMS) {
 
481
                                switch (cmd) {
 
482
                                case DCP:
 
483
                                        track->copy = 1; break;
 
484
                                case FOURCH:
 
485
                                        track->four_channel_audio = 1; break;
 
486
                                case PRE:
 
487
                                        track->pre_emphasis = 1; break;
 
488
                                case SCMS:
 
489
                                        err_warn("serial copy management "
 
490
                                                 "system flag not supported "
 
491
                                                 "by cdrdao"); break;
 
492
                                default:
 
493
                                        err_fail("Should not get here");
 
494
                                }
 
495
                                cmd = get_command(f);
 
496
                        }
 
497
                        /* current non-FLAG command is already in cmd, so
 
498
                           avoid get_command() call below */
 
499
                        continue; break;
 
500
                case PREGAP:
 
501
                        if (track->pregap != -1)
 
502
                                err_fail("PREGAP allowed only once per track");
 
503
                        if (get_string(f, timecode_buffer, TCBUFLEN - 1) < 1)
 
504
                                err_earlyend();
 
505
                        track->pregap = tc2fr(timecode_buffer);
 
506
                        if (track->pregap == -1)
 
507
                                err_fail("Timecode out of range");
 
508
                        track->pregap_data_from_file = 0;
 
509
                        break;
 
510
                case POSTGAP:
 
511
                        if (track->postgap != -1)
 
512
                                err_fail("POSTGAP allowed only once per track");
 
513
                        if (get_string(f, timecode_buffer, TCBUFLEN - 1) < 1)
 
514
                                err_earlyend();
 
515
                        track->postgap = tc2fr(timecode_buffer);
 
516
                        if (track->postgap == -1)
 
517
                                err_fail("Timecode out of range");
 
518
                        break;
 
519
                case INDEX:
 
520
                        if (get_string(f, timecode_buffer, 2) < 1)
 
521
                                err_earlyend();
 
522
                        n = atoi(timecode_buffer);
 
523
                        if (n < 0 || n > 99)
 
524
                                err_fail("Index out of range");
 
525
 
 
526
                        /* Index 0 is track pregap and Index 1 is start
 
527
                           of track. Index 2 to 99 are the true subindexes
 
528
                           and only allowed if the preceding one was there
 
529
                           before */
 
530
                        switch (n) {
 
531
                        case 0:
 
532
                                if (track->start != -1)
 
533
                                        err_fail("Indexes must be sequential");
 
534
                                if (track->pregap != -1)
 
535
                                        err_fail("PREGAP allowed only once "
 
536
                                                 "per track");
 
537
                                if (get_string(f, timecode_buffer,
 
538
                                               TCBUFLEN - 1) < 1)
 
539
                                        err_earlyend();
 
540
                                /* This is only a temporary value until
 
541
                                   index 01 is read */
 
542
                                track->pregap = tc2fr(timecode_buffer);
 
543
                                if (track->pregap == -1)
 
544
                                        err_fail("Timecode out of range");
 
545
                                track->pregap_data_from_file = 1;
 
546
                                break;
 
547
                        case 1:
 
548
                                if (track->start != -1)
 
549
                                        err_fail("Each index allowed only "
 
550
                                                 "once per track");
 
551
                                if (get_string(f, timecode_buffer,
 
552
                                               TCBUFLEN - 1) < 1)
 
553
                                        err_fail("Missing timecode");
 
554
                                track->start = tc2fr(timecode_buffer);
 
555
                                if (track->start == -1)
 
556
                                        err_fail("Timecode out of range");
 
557
                                /* Fix the pregap value */
 
558
                                if (track->pregap_data_from_file)
 
559
                                        track->pregap = track->start
 
560
                                                        - track->pregap;
 
561
                                break;
 
562
                        case 2:
 
563
                                if (track->start == -1)
 
564
                                        err_fail("Indexes must be sequential");
 
565
                                if (track->indexes[n - 2] != -1)
 
566
                                        err_fail("Each index allowed only "
 
567
                                                 "once per track");
 
568
                                if (get_string(f, timecode_buffer,
 
569
                                               TCBUFLEN - 1) < 1)
 
570
                                        err_fail("Missing timecode");
 
571
                                track->indexes[n - 2] = tc2fr(timecode_buffer);
 
572
                                if (track->indexes[n - 2] == -1)
 
573
                                        err_fail("Timecode out of range");
 
574
                                break;
 
575
                        default:        /* the other 97 indexes */
 
576
                                /* check if previous index is there */
 
577
                                if (track->indexes[n - 3] == -1)
 
578
                                        err_fail("Indexes must be sequential");
 
579
                                if (track->indexes[n - 2] != -1)
 
580
                                        err_fail("Each index allowed only "
 
581
                                                 "once per track");
 
582
                                if (get_string(f, timecode_buffer,
 
583
                                               TCBUFLEN - 1) < 1)
 
584
                                        err_fail("Missing timecode");
 
585
                                track->indexes[n - 2] = tc2fr(timecode_buffer);
 
586
                                if (track->indexes[n - 2] == -1)
 
587
                                        err_fail("Timecode out of range");
 
588
                                break;
 
589
                        }
 
590
                        break;
 
591
                case FILECMD:
 
592
                        if (get_string(f, file, FILENAMELEN) < 1)
 
593
                                err_earlyend();
 
594
 
 
595
                        switch (cmd = get_command(f)) {
 
596
                        case MOTOROLA:
 
597
                                err_warn("big endian binary file");
 
598
                        case BINARY:
 
599
                                filetype = BINARY; break;
 
600
                        case AIFF: case MP3:
 
601
                                err_warn("AIFF and MP3 not supported by "
 
602
                                         "cdrdao");
 
603
                        case WAVE:
 
604
                                if (wavefile) {
 
605
                                        strncpy(file, wavefile, FILENAMELEN);
 
606
                                        file[FILENAMELEN] = '\0';
 
607
                                }
 
608
                                filetype = WAVE; break;
 
609
                        default:
 
610
                                err_fail("Unsupported file type");
 
611
                        }
 
612
                        break;
 
613
                default:
 
614
                        err_fail("Command not allowed in track spec");
 
615
                        break;
 
616
                }
 
617
                
 
618
                cmd = get_command(f);
 
619
        }
 
620
 
 
621
        check_cutting_binary(track);
 
622
 
 
623
        return cs;
 
624
}
 
625
 
 
626
/* Deduce the disc session type from the track modes */
 
627
static enum session_type
 
628
determine_session_type(struct trackspec *list)
 
629
{
 
630
        struct trackspec *track = list;
 
631
        /* set to true if track of corresponding type is found */
 
632
        int audio = 0;
 
633
        int mode1 = 0;
 
634
        int mode2 = 0;
 
635
 
 
636
        while (track != NULL) {
 
637
                switch (track->mode) {
 
638
                case AUDIO:
 
639
                        audio = 1; break;
 
640
                case MODE1: case MODE1_RAW:
 
641
                        mode1 = 1; break;
 
642
                case MODE2: case MODE2_RAW:
 
643
                        mode2 = 1; break;
 
644
                default:        /* should never get here */
 
645
                        err_fail2("Dont know how this could happen, but here "
 
646
                                 "is a track with an unknown mode :|");
 
647
                }
 
648
                track = track->next;
 
649
        }
 
650
 
 
651
        /* CD_DA        only audio
 
652
         * CD_ROM       only mode1 with or without audio
 
653
         * CD_ROM_XA    only mode2 with or without audio
 
654
         */
 
655
        if (audio && !mode1 && !mode2)
 
656
                return CD_DA;
 
657
        else if (audio && mode1 && !mode2 || !audio && mode1 && !mode2)
 
658
                return CD_ROM;
 
659
        else if (audio && !mode1 && mode2 || !audio && !mode1 && mode2)
 
660
                return CD_ROM_XA;
 
661
        else
 
662
                return INVALID;
 
663
}
 
664
 
 
665
/* Return true if cuesheet contains any CD-Text data */
 
666
static int
 
667
contains_cdtext(struct cuesheet *cs)
 
668
{
 
669
        struct trackspec *track = cs->tracklist;
 
670
 
 
671
        if (cs->title[0] != '\0' || cs->performer[0] != '\0'
 
672
            || cs->songwriter[0] != '\0')
 
673
                return 1;
 
674
 
 
675
        while (track) {
 
676
                if (track->title[0] != '\0' || track->performer[0] != '\0'
 
677
                    || track->songwriter[0] != '\0')
 
678
                        return 1;
 
679
                track = track->next;
 
680
        }
 
681
 
 
682
        return 0;
 
683
}
 
684
 
 
685
/* fprintf() with indentation. The argument indent is the number of spaces
 
686
   to print per level. E.g. with indent=4 and level=3 there are 12 spaces
 
687
   printed. Every eight spaces are replaced by a single tabulator. The
 
688
   return value is the return value of fprintf(). */
 
689
static int
 
690
ifprintf(FILE *f, int indent, int level, const char *format, ...)
 
691
{
 
692
        va_list ap;
 
693
        int fprintf_return = 0;
 
694
        int tabs = indent * level / 8;
 
695
        int spaces = indent * level % 8;
 
696
        int i;
 
697
 
 
698
        for (i = 0; i < tabs; i++)
 
699
                fputc('\t', f);
 
700
        for (i = 0; i < spaces; i++)
 
701
                fputc(' ', f);
 
702
 
 
703
        va_start(ap, format);
 
704
        fprintf_return = vfprintf(f, format, ap);
 
705
        va_end(ap);
 
706
 
 
707
        return fprintf_return;
 
708
}
 
709
 
 
710
/* Write a track to the file f. The arguments i and l are the indentation
 
711
   amount and level (see ifprintf above). Do not write CD-Text data if
 
712
   cdtext is zero. */
 
713
static void
 
714
write_track(struct trackspec *tr, FILE *f, int i, int l, int cdtext)
 
715
{
 
716
        char timecode_buffer[TCBUFLEN];
 
717
        long start = 0, len = 0;
 
718
        int j = 0;
 
719
 
 
720
        fprintf(f, "\n");
 
721
        ifprintf(f, i, l++, "TRACK ");
 
722
        switch(tr->mode) {
 
723
        case AUDIO:     fprintf(f, "AUDIO\n"); break;
 
724
        case MODE1:     fprintf(f, "MODE1\n"); break;
 
725
        case MODE1_RAW: fprintf(f, "MODE1_RAW\n"); break;
 
726
        case MODE2:     fprintf(f, "MODE2\n"); break;
 
727
        case MODE2_RAW: fprintf(f, "MODE2_RAW\n"); break;
 
728
        default:        err_fail2("Unknown track mode"); /* cant get here */
 
729
        }
 
730
 
 
731
        /* Flags and ISRC */
 
732
        if (tr->copy)
 
733
                ifprintf(f, i, l, "COPY\n");
 
734
        if (tr->pre_emphasis)
 
735
                ifprintf(f, i, l, "PRE_EMPHASIS\n");
 
736
        if (tr->four_channel_audio)
 
737
                ifprintf(f, i, l, "FOUR_CHANNEL_AUDIO\n");
 
738
        if (tr->isrc[0] != '\0')
 
739
                ifprintf(f, i, l, "ISRC \"%s\"\n", tr->isrc);
 
740
 
 
741
        /* CD-Text data */
 
742
        if (cdtext && (tr->title[0] != '\0' || tr->performer[0] != '\0'
 
743
                       || tr->songwriter[0] != '\0')) {
 
744
                ifprintf(f, i, l++, "CD_TEXT {\n");
 
745
                ifprintf(f, i, l++, "LANGUAGE 0 {\n");
 
746
                if (tr->title[0] != '\0')
 
747
                        ifprintf(f, i, l, "TITLE \"%s\"\n", tr->title);
 
748
                if (tr->performer[0] != '\0')
 
749
                        ifprintf(f, i, l, "PERFORMER \"%s\"\n", tr->performer);
 
750
                if (tr->songwriter[0] != '\0')
 
751
                        ifprintf(f, i, l, "SONGWRITER \"%s\"\n",
 
752
                                 tr->songwriter);
 
753
                ifprintf(f, i, --l, "}\n");     /* LANGUAGE 0 { */
 
754
                ifprintf(f, i, --l, "}\n");     /* CD_TEXT { */
 
755
        }
 
756
 
 
757
        /* Pregap with zero data */
 
758
        if (tr->pregap != -1 && !tr->pregap_data_from_file) {
 
759
                if (fr2tc(timecode_buffer, tr->pregap) == -1)
 
760
                        err_fail2("Pregap out of range");
 
761
                ifprintf(f, i, l, "PREGAP %s\n", timecode_buffer);
 
762
        }
 
763
 
 
764
        /* Specify the file */
 
765
        start = 0;
 
766
        if (tr->mode == AUDIO) {
 
767
                ifprintf(f, i, l, "AUDIOFILE \"%s\" ", tr->filename);
 
768
                if (tr->start != -1) {
 
769
                        if (tr->pregap_data_from_file) {
 
770
                                start = tr->start - tr->pregap;
 
771
                        } else
 
772
                                start = tr->start;
 
773
                }
 
774
                if (fr2tc(timecode_buffer, start) == -1)
 
775
                        err_fail2("Track start out of range");
 
776
                fprintf(f, "%s", timecode_buffer);
 
777
        } else
 
778
                ifprintf(f, i, l, "DATAFILE \"%s\"", tr->filename);
 
779
 
 
780
        /* If next track has the same filename and specified a start
 
781
           value use the difference between start of this and start of
 
782
           the next track as the length of the current track */
 
783
        if (tr->next
 
784
            && strcmp(tr->filename, tr->next->filename) == 0
 
785
            && tr->next->start != -1) {
 
786
                if (tr->next->pregap_data_from_file)
 
787
                        len = tr->next->start - tr->next->pregap
 
788
                              - start;
 
789
                else
 
790
                        len = tr->next->start - start;
 
791
                if (fr2tc(timecode_buffer, len) == -1)
 
792
                        err_fail2("Track length out of range");
 
793
                fprintf(f, " %s\n", timecode_buffer);
 
794
        } else
 
795
                fprintf(f, "\n");
 
796
 
 
797
        /* Pregap with data from file */
 
798
        if (tr->pregap_data_from_file) {
 
799
                if (fr2tc(timecode_buffer, tr->pregap) == -1)
 
800
                        err_fail2("Pregap out of range");
 
801
                ifprintf(f, i, l, "START %s\n", timecode_buffer);
 
802
        }
 
803
 
 
804
        /* Postgap */
 
805
        if (tr->postgap != -1) {
 
806
                if (fr2tc(timecode_buffer, tr->postgap) == -1)
 
807
                        err_fail2("Postgap out of range");
 
808
                if (tr->mode == AUDIO)
 
809
                        ifprintf(f, i, l, "SILENCE %s\n", timecode_buffer);
 
810
                else
 
811
                        ifprintf(f, i, l, "ZERO %s\n", timecode_buffer);
 
812
        }
 
813
 
 
814
        /* Indexes */
 
815
        while (tr->indexes[j] != -1 && i < NUM_OF_INDEXES) {
 
816
                if (fr2tc(timecode_buffer, tr->indexes[j++]) == -1)
 
817
                        err_fail2("Index out of range");
 
818
                ifprintf(f, i, l, "INDEX %s\n", timecode_buffer);
 
819
        }
 
820
 
 
821
}
 
822
 
 
823
/* Write the cuesheet cs to the file named in tocfile. If tocfile is NULL
 
824
   write to stdout. Do not write CD-Text data if cdt is zero. */
 
825
void
 
826
write_toc(const char *tocfile, struct cuesheet *cs, int cdt)
 
827
{
 
828
        FILE *f = stdout;
 
829
        int i = 4;              /* number of chars for indentation */
 
830
        int l = 0;              /* current leven of indentation */
 
831
        int cdtext = contains_cdtext(cs) && cdt;
 
832
        struct trackspec *track = cs->tracklist;
 
833
 
 
834
        if (tocfile != NULL)
 
835
                if ((f = fopen(tocfile, "w")) == NULL) {
 
836
                        fprintf(stderr, "%s: Could not open file \"%s\" for "
 
837
                        "writing: %s\n", progname, tocfile, strerror(errno));
 
838
                exit(EXIT_FAILURE);
 
839
        }
 
840
 
 
841
        if ((cs->type = determine_session_type(cs->tracklist)) == INVALID)
 
842
                err_fail2("Invalid combination of track modes");
 
843
 
 
844
        ifprintf(f, i, l, "// Generated by cue2toc 0.2\n");
 
845
        ifprintf(f, i, l, "// Report bugs to <dermatsch@gmx.de>\n");
 
846
 
 
847
        if (cs->catalog[0] != '\0')
 
848
                ifprintf(f, i, l, "CATALOG \"%s\"\n", cs->catalog);
 
849
 
 
850
        switch (cs->type) {
 
851
        case CD_DA:     ifprintf(f, i, l, "CD_DA\n"); break;
 
852
        case CD_ROM:    ifprintf(f, i, l, "CD_ROM\n"); break;
 
853
        case CD_ROM_XA: ifprintf(f, i, l, "CD_ROM_XA\n"); break;
 
854
        default:        err_fail2("Should never get here");
 
855
        }
 
856
 
 
857
        if (cdtext) {
 
858
                ifprintf(f, i, l++, "CD_TEXT {\n");
 
859
                ifprintf(f, i, l++, "LANGUAGE_MAP {\n");
 
860
                ifprintf(f, i, l, "0 : EN\n");
 
861
                ifprintf(f, i, --l, "}\n");
 
862
                ifprintf(f, i, l++, "LANGUAGE 0 {\n");
 
863
                if (cs->title[0] != '\0')
 
864
                        ifprintf(f, i, l, "TITLE \"%s\"\n", cs->title);
 
865
                if (cs->performer[0] != '\0')
 
866
                        ifprintf(f, i, l, "PERFORMER \"%s\"\n", cs->performer);
 
867
                if (cs->songwriter[0] != '\0')
 
868
                        ifprintf(f, i, l, "SONGWRITER \"%s\"\n",
 
869
                                 cs->songwriter);
 
870
                ifprintf(f, i, --l, "}\n");
 
871
                ifprintf(f, i, --l, "}\n");
 
872
        }
 
873
 
 
874
        while (track) {
 
875
                write_track(track, f, i, l, cdtext);
 
876
                track = track->next;
 
877
        }
 
878
}