/* cdrdao - write audio CD-Rs in disc-at-once mode
*
* Copyright (C) 1998-2001 Andreas Mueller <andreas@daneb.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#header <<
#include <config.h>
#include <stdlib.h>
#include "Toc.h"
#include "util.h"
#include "log.h"
#include "CdTextItem.h"
>>
<<
#include "CdTextContainer.h"
#include "TocLexerBase.h"
// Maximum length of binary data for CD-TEXT
#define MAX_CD_TEXT_DATA_LEN (256 * 12)
/* Use 'ANTLRCommonToken' as 'ANTLRToken' to be compatible with some bug
* fix releases of PCCTS-1.33. The drawback is that the token length is
* limited to 100 characters. This might be a problem for long file names.
* Define 'USE_ANTLRCommonToken' to 0 if you want to use the dynamic token
* which handles arbitrary token lengths (there'll be still a limitation in
* the lexer code but that's more than 100 characters). In this case it might
* be necessary to adjust the types of the member functions to the types in
* 'ANTLRAbstractToken' defined in PCCTSDIR/h/AToken.h'.
*/
#define USE_ANTLRCommonToken 0
#if USE_ANTLRCommonToken
typedef ANTLRCommonToken ANTLRToken;
#else
class ANTLRToken : public ANTLRRefCountToken {
private:
ANTLRTokenType type_;
int line_;
ANTLRChar *text_;
public:
ANTLRToken(ANTLRTokenType t, ANTLRChar *s)
: ANTLRRefCountToken(t, s)
{
setType(t);
line_ = 0;
setText(s);
}
ANTLRToken()
{
setType((ANTLRTokenType)0);
line_ = 0;
setText("");
}
virtual ~ANTLRToken() { delete[] text_; }
#if 0
// use this for basic PCCTS-1.33 release
ANTLRTokenType getType() { return type_; }
virtual int getLine() { return line_; }
ANTLRChar *getText() { return text_; }
void setText(ANTLRChar *s) { text_ = strdupCC(s); }
#else
// use this for PCCTS-1.33 bug fix releases
ANTLRTokenType getType() const { return type_; }
virtual int getLine() const { return line_; }
ANTLRChar *getText() const { return text_; }
void setText(const ANTLRChar *s) { text_ = strdupCC(s); }
#endif
void setType(ANTLRTokenType t) { type_ = t; }
void setLine(int line) { line_ = line; }
virtual ANTLRAbstractToken *makeToken(ANTLRTokenType tt, ANTLRChar *txt,
int line)
{
ANTLRAbstractToken *t = new ANTLRToken(tt,txt);
t->setLine(line);
return t;
}
};
#endif
>>
<<
class TocLexer : public TocLexerBase {
public:
TocLexer(DLGInputStream *in, unsigned bufsize=2000)
: TocLexerBase(in, bufsize) { parser_ = NULL; }
virtual ANTLRTokenType erraction();
TocParserGram *parser_;
};
>>
#lexclass START
#token Eof "@"
#token "[\t\r\ ]+" << skip(); >>
#token Comment "//~[\n@]*" << skip(); >>
#token "\n" << newline(); skip(); >>
#token BeginString "\"" << mode(STRING); >>
#token Integer "[0-9]+"
#token TrackDef "TRACK"
#token FirstTrackNo "FIRST_TRACK_NO"
#token Audio "AUDIO"
#token Mode0 "MODE0"
#token Mode1 "MODE1"
#token Mode1Raw "MODE1_RAW"
#token Mode2 "MODE2"
#token Mode2Raw "MODE2_RAW"
#token Mode2Form1 "MODE2_FORM1"
#token Mode2Form2 "MODE2_FORM2"
#token Mode2FormMix "MODE2_FORM_MIX"
#token SubChanRw "RW"
#token SubChanRwRaw "RW_RAW"
#token Index "INDEX"
#token Catalog "CATALOG"
#token Isrc "ISRC"
#token No "NO"
#token Copy "COPY"
#token PreEmphasis "PRE_EMPHASIS"
#token TwoChannel "TWO_CHANNEL_AUDIO"
#token FourChannel "FOUR_CHANNEL_AUDIO"
#tokclass AudioFile { "AUDIOFILE" "FILE" }
#token DataFile "DATAFILE"
#token Fifo "FIFO"
#token Silence "SILENCE"
#token Zero "ZERO"
#token Pregap "PREGAP"
#token Start "START"
#token End "END"
#token TocTypeCdda "CD_DA"
#token TocTypeCdrom "CD_ROM"
#token TocTypeCdromXa "CD_ROM_XA"
#token TocTypeCdi "CD_I"
#token Swap "SWAP"
#token CdText "CD_TEXT"
#token Language "LANGUAGE"
#token LanguageMap "LANGUAGE_MAP"
#token Title "TITLE"
#token Performer "PERFORMER"
#token Songwriter "SONGWRITER"
#token Composer "COMPOSER"
#token Arranger "ARRANGER"
#token Message "MESSAGE"
#token DiscId "DISC_ID"
#token Genre "GENRE"
#token TocInfo1 "TOC_INFO1"
#token TocInfo2 "TOC_INFO2"
#token Reserved1 "RESERVED1"
#token Reserved2 "RESERVED2"
#token Reserved3 "RESERVED3"
#token Reserved4 "RESERVED4"
#token UpcEan "UPC_EAN"
#token SizeInfo "SIZE_INFO"
#token LangEn "EN"
#lexclass STRING
#token EndString "\"" << mode(START); >>
#token StringQuote "\\\""
#token StringOctal "\\[0-9][0-9][0-9]"
#token String "\\"
#token String "[ ]+"
#token String "~[\\\n\"\t ]*"
#lexclass START
class TocParserGram {
<<
public:
const char *filename_;
int error_;
void syn(_ANTLRTokenPtr tok, ANTLRChar *egroup, SetWordType *eset,
ANTLRTokenType etok, int k);
>>
toc > [ Toc *t ]
: << $t = new Toc;
Track *tr = NULL;
int lineNr = 0;
char *catalog = NULL;
Toc::TocType toctype;
int firsttrack, firstLine;
>>
( Catalog string > [ catalog ]
<< if (catalog != NULL) {
if ($t->catalog(catalog) != 0) {
log_message(-2, "%s:%d: Illegal catalog number: %s.\n",
filename_, $1->getLine(), catalog);
error_ = 1;
}
delete[] catalog;
catalog = NULL;
}
>>
| tocType > [ toctype ]
<< $t->tocType(toctype); >>
)*
{ FirstTrackNo integer > [ firsttrack, firstLine ]
<< if (firsttrack > 0 && firsttrack < 100) {
$t->firstTrackNo(firsttrack);
} else {
log_message(-2, "%s:%d: Illegal track number: %d\n", filename_,
firstLine, firsttrack);
error_ = 1;
}
>>
}
{ cdTextGlobal [ $t->cdtext_ ] }
( track > [ tr, lineNr ]
<< if (tr != NULL) {
if ($t->append(tr) != 0) {
log_message(-2, "%s:%d: First track must not have a pregap.\n",
filename_, lineNr);
error_ = 1;
}
delete tr, tr = NULL;
}
>>
)+
Eof
;
// fail action
<< delete $t, $t = NULL;
delete[] catalog;
>>
track > [ Track *tr, int lineNr ]
: << $tr = NULL;
$lineNr = 0;
SubTrack *st = NULL;
char *isrcCode = NULL;
TrackData::Mode trackType;
TrackData::SubChannelMode subChanType = TrackData::SUBCHAN_NONE;
Msf length;
Msf indexIncrement;
Msf pos;
int posGiven = 0;
Msf startPos; // end of pre-gap
Msf endPos; // start if post-gap
int startPosLine = 0;
int endPosLine = 0;
int lineNr = 0;
int flag = 1;
>>
TrackDef << $lineNr = $1->getLine(); >>
trackMode > [ trackType ]
{ subChannelMode > [ subChanType ] }
<< $tr = new Track(trackType, subChanType); >>
( Isrc string > [ isrcCode ]
<< if (isrcCode != NULL) {
if ($tr->isrc(isrcCode) != 0) {
log_message(-2, "%s:%d: Illegal ISRC code: %s.\n",
filename_, $1->getLine(), isrcCode);
error_ = 1;
}
delete[] isrcCode;
}
>>
| { No << flag = 0; >> } Copy
<< $tr->copyPermitted(flag); flag = 1; >>
| { No << flag = 0; >> } PreEmphasis
<< $tr->preEmphasis(flag); flag = 1; >>
| TwoChannel
<< $tr->audioType(0); >>
| FourChannel
<< $tr->audioType(1); >>
)*
{ cdTextTrack [ $tr->cdtext_ ] }
{ Pregap msf > [ length ]
<< if (length.lba() == 0) {
log_message(-2, "%s:%d: Length of pregap is zero.\n",
filename_, $1->getLine());
error_ = 1;
}
else {
if (trackType == TrackData::AUDIO) {
$tr->append(SubTrack(SubTrack::DATA,
TrackData(length.samples())));
}
else {
$tr->append(SubTrack(SubTrack::DATA,
TrackData(trackType, subChanType,
length.lba() * TrackData::dataBlockSize(trackType, subChanType))));
}
startPos = $tr->length();
startPosLine = $1->getLine();
}
>>
}
( subTrack [ trackType, subChanType ] > [ st, lineNr ]
<<
if (st != NULL && $tr->append(*st) == 2) {
log_message(-2,
"%s:%d: Mixing of FILE/AUDIOFILE/SILENCE and DATAFILE/ZERO statements not allowed.", filename_, lineNr);
log_message(-2,
"%s:%d: PREGAP acts as SILENCE in audio tracks.",
filename_, lineNr);
error_ = 1;
}
delete st, st = NULL;
>>
| Start << posGiven = 0; >> { msf > [ pos ] << posGiven = 1; >> }
<< if (startPosLine != 0) {
log_message(-2,
"%s:%d: Track start (end of pre-gap) already defined.\n",
filename_, $1->getLine());
error_ = 1;
}
else {
if (!posGiven) {
pos = $tr->length(); // retrieve current position
}
startPos = pos;
startPosLine = $1->getLine();
}
pos = Msf(0);
>>
| End << posGiven = 0; >> { msf > [ pos ] << posGiven = 1; >> }
<< if (endPosLine != 0) {
log_message(-2,
"%s:%d: Track end (start of post-gap) already defined.\n",
filename_, $1->getLine());
error_ = 1;
}
else {
if (!posGiven) {
pos = $tr->length(); // retrieve current position
}
endPos = pos;
endPosLine = $1->getLine();
}
pos = Msf(0);
>>
)+
// set track start (end of pre-gap) and check for minimal track length
<< if (startPosLine != 0 && $tr->start(startPos) != 0) {
log_message(-2,
"%s:%d: START %s behind or at track end.\n", filename_,
startPosLine, startPos.str());
error_ = 1;
}
>>
( Index msf > [ indexIncrement ]
<< if ($tr != NULL) {
switch ($tr->appendIndex(indexIncrement)) {
case 1:
log_message(-2, "%s:%d: More than 98 index increments.\n",
filename_, $1->getLine());
error_ = 1;
break;
case 2:
log_message(-2, "%s:%d: Index beyond track end.\n",
filename_, $1->getLine());
error_ = 1;
break;
case 3:
log_message(-2, "%s:%d: Index at start of track.\n",
filename_, $1->getLine());
error_ = 1;
break;
}
}
>>
)*
// set track end (start of post-gap)
<< if (endPosLine != 0) {
switch ($tr->end(endPos)) {
case 1:
log_message(-2, "%s:%d: END %s behind or at track end.\n",
filename_, endPosLine, endPos.str());
error_ = 1;
break;
case 2:
log_message(-2, "%s:%d: END %s within pre-gap.\n",
filename_, endPosLine, endPos.str());
error_ = 1;
break;
case 3:
log_message(-2,
"%s:%d: END %s: Cannot create index mark for post-gap.\n",
filename_, endPosLine, endPos.str());
error_ = 1;
break;
}
}
>>
;
// fail action
<< delete $tr, $tr = NULL;
delete[] isrcCode;
>>
subTrack < [ TrackData::Mode trackType, TrackData::SubChannelMode subChanType ] > [ SubTrack *st, int lineNr ]
: << $st = NULL;
$lineNr = 0;
char *filename = NULL;
unsigned long start = 0;
unsigned long len = 0;
long offset = 0;
TrackData::Mode dMode;
int swapSamples = 0;
>>
( AudioFile string > [ filename ]
{ Swap << swapSamples = 1; >> }
{ "#" sLong > [ offset ] }
samples > [start] { samples > [len] }
<< $st = new SubTrack(SubTrack::DATA,
TrackData(filename, offset, start, len));
$st->swapSamples(swapSamples);
$lineNr = $1->getLine();
if (trackType != TrackData::AUDIO) {
log_message(-2, "%s:%d: FILE/AUDIOFILE statements are only allowed for audio tracks.", filename_, $1->getLine());
error_ = 1;
}
if (subChanType != TrackData::SUBCHAN_NONE) {
log_message(-2, "%s:%d: FILE/AUDIOFILE statements are only allowed for audio tracks without sub-channel mode.", filename_, $1->getLine());
error_ = 1;
}
>>
| DataFile string > [ filename ]
<< dMode = $trackType; >>
// { dataMode > [ dMode ] }
{ "#" sLong > [ offset ] }
{ dataLength [ dMode, $subChanType ] > [ len ] }
<< $st = new SubTrack(SubTrack::DATA, TrackData(dMode, $subChanType,
filename,
offset, len));
$lineNr = $1->getLine();
>>
| Fifo string > [ filename ]
dataLength [$trackType, $subChanType ] > [ len ]
<< $st = new SubTrack(SubTrack::DATA, TrackData($trackType,
$subChanType,
filename, len));
>>
| Silence samples > [len]
<< $st = new SubTrack(SubTrack::DATA, TrackData(len));
$lineNr = $1->getLine();
if (len == 0) {
log_message(-2, "%s:%d: Length of silence is 0.\n",
filename_, $lineNr);
error_ = 1;
}
if (trackType != TrackData::AUDIO) {
log_message(-2, "%s:%d: SILENCE statements are only allowed for audio tracks.", filename_, $1->getLine());
error_ = 1;
}
if (subChanType != TrackData::SUBCHAN_NONE) {
log_message(-2, "%s:%d: SILENCE statements are only allowed for audio tracks without sub-channel mode.", filename_, $1->getLine());
error_ = 1;
}
>>
| Zero
<< dMode = $trackType; >>
{ dataMode > [ dMode ] }
{ subChannelMode > [ $subChanType ] }
dataLength [ dMode, $subChanType ] > [ len ]
<< $st = new SubTrack(SubTrack::DATA, TrackData(dMode, $subChanType,
len));
$lineNr = $1->getLine();
if (len == 0) {
log_message(-2, "%s:%d: Length of zero data is 0.\n",
filename_, $lineNr);
error_ = 1;
}
>>
)
<< if ($st != NULL && $st->length() == 0) {
// try to determine length
if ($st->determineLength() != 0) {
log_message(-2, "%s:%d: Cannot determine length of track data specification.",
filename_, $lineNr);
error_ = 1;
}
}
>>
;
// fail action
<< delete $st, $st = NULL;
delete[] filename;
>>
string > [ char *ret ]
: << $ret = strdupCC("");
char *s;
char buf[2];
>>
<< buf[1] = 0; >>
BeginString
( ( String << s = strdup3CC($ret, $1->getText(), NULL); >>
| StringQuote << s = strdup3CC($ret, "\"", NULL); >>
| StringOctal << buf[0] = strtol($1->getText() + 1, NULL, 8);
s = strdup3CC($ret, buf, NULL);
>>
)
<< delete[] $ret;
$ret = s;
>>
)+
EndString
;
stringEmpty > [ char *ret ]
: << $ret = strdupCC("");
char *s;
char buf[2];
>>
<< buf[1] = 0; >>
BeginString
( ( String << s = strdup3CC($ret, $1->getText(), NULL); >>
| StringQuote << s = strdup3CC($ret, "\"", NULL); >>
| StringOctal << buf[0] = strtol($1->getText() + 1, NULL, 8);
s = strdup3CC($ret, buf, NULL);
>>
)
<< delete[] $ret;
$ret = s;
>>
)*
EndString
;
uLong > [ unsigned long l ]
: << $l = 0; >>
Integer << $l = strtoul($1->getText(), NULL, 10); >>
;
sLong > [ long l ]
: << $l = 0; >>
Integer << $l = strtol($1->getText(), NULL, 10); >>
;
integer > [ int i, int lineNr ]
: << $i = 0; >>
Integer << $i = atol($1->getText()); $lineNr = $1->getLine(); >>
;
msf > [ Msf m ]
: << int min = 0;
int sec = 0;
int frac = 0;
int err = 0;
int minLine;
int secLine;
int fracLine;
>>
integer > [min, minLine] ":" integer > [sec, secLine]
":" integer > [frac, fracLine]
<< if (min < 0) {
log_message(-2, "%s:%d: Illegal minute field: %d\n", filename_,
minLine, min);
err = error_ = 1;
}
if (sec < 0 || sec > 59) {
log_message(-2, "%s:%d: Illegal second field: %d\n", filename_,
secLine, sec);
err = error_ = 1;
}
if (frac < 0 || frac > 74) {
log_message(-2, "%s:%d: Illegal fraction field: %d\n", filename_,
fracLine, frac);
err = error_ = 1;
}
if (err != 0) {
$m = Msf(0);
}
else {
$m = Msf(min, sec, frac);
}
>>
;
samples > [ unsigned long s ]
: << Msf m; >>
( msf > [ m ] << $s = m.samples(); >>
| uLong > [ $s ]
)
;
// fail action
<< $s = 0; >>
dataLength [ TrackData::Mode mode, TrackData::SubChannelMode sm] > [ unsigned long len ]
: << Msf m;
unsigned long blen;
blen = TrackData::dataBlockSize(mode, sm);
>>
( msf > [ m] << $len = m.lba() * blen; >>
| uLong > [ $len ]
)
;
// fail action
<< $len = 0; >>
dataMode > [ TrackData::Mode m ]
:
( Audio << $m = TrackData::AUDIO; >>
| Mode0 << $m = TrackData::MODE0; >>
| Mode1 << $m = TrackData::MODE1; >>
| Mode1Raw << $m = TrackData::MODE1_RAW; >>
| Mode2 << $m = TrackData::MODE2; >>
| Mode2Raw << $m = TrackData::MODE2_RAW; >>
| Mode2Form1 << $m = TrackData::MODE2_FORM1; >>
| Mode2Form2 << $m = TrackData::MODE2_FORM2; >>
| Mode2FormMix << $m = TrackData::MODE2_FORM_MIX; >>
)
;
trackMode > [ TrackData::Mode m ]
:
( Audio << $m = TrackData::AUDIO; >>
| Mode1 << $m = TrackData::MODE1; >>
| Mode1Raw << $m = TrackData::MODE1_RAW; >>
| Mode2 << $m = TrackData::MODE2; >>
| Mode2Raw << $m = TrackData::MODE2_RAW; >>
| Mode2Form1 << $m = TrackData::MODE2_FORM1; >>
| Mode2Form2 << $m = TrackData::MODE2_FORM2; >>
| Mode2FormMix << $m = TrackData::MODE2_FORM_MIX; >>
)
;
subChannelMode > [ TrackData::SubChannelMode m ]
:
( SubChanRw << $m = TrackData::SUBCHAN_RW; >>
| SubChanRwRaw << $m = TrackData::SUBCHAN_RW_RAW; >>
)
;
tocType > [ Toc::TocType t ]
: ( TocTypeCdda << $t = Toc::CD_DA; >>
| TocTypeCdrom << $t = Toc::CD_ROM; >>
| TocTypeCdromXa << $t = Toc::CD_ROM_XA; >>
| TocTypeCdi << $t = Toc::CD_I; >>
)
;
packType > [ CdTextItem::PackType t, int lineNr ]
: ( Title << $t = CdTextItem::CDTEXT_TITLE; $lineNr = $1->getLine(); >>
| Performer << $t = CdTextItem::CDTEXT_PERFORMER; $lineNr = $1->getLine(); >>
| Songwriter << $t = CdTextItem::CDTEXT_SONGWRITER; $lineNr = $1->getLine(); >>
| Composer << $t = CdTextItem::CDTEXT_COMPOSER; $lineNr = $1->getLine(); >>
| Arranger << $t = CdTextItem::CDTEXT_ARRANGER; $lineNr = $1->getLine(); >>
| Message << $t = CdTextItem::CDTEXT_MESSAGE; $lineNr = $1->getLine(); >>
| DiscId << $t = CdTextItem::CDTEXT_DISK_ID; $lineNr = $1->getLine(); >>
| Genre << $t = CdTextItem::CDTEXT_GENRE; $lineNr = $1->getLine(); >>
| TocInfo1 << $t = CdTextItem::CDTEXT_TOC_INFO1; $lineNr = $1->getLine(); >>
| TocInfo2 << $t = CdTextItem::CDTEXT_TOC_INFO2; $lineNr = $1->getLine(); >>
| Reserved1 << $t = CdTextItem::CDTEXT_RES1; $lineNr = $1->getLine(); >>
| Reserved2 << $t = CdTextItem::CDTEXT_RES2; $lineNr = $1->getLine(); >>
| Reserved3 << $t = CdTextItem::CDTEXT_RES3; $lineNr = $1->getLine(); >>
| Reserved4 << $t = CdTextItem::CDTEXT_RES4; $lineNr = $1->getLine(); >>
| UpcEan << $t = CdTextItem::CDTEXT_UPCEAN_ISRC; $lineNr = $1->getLine(); >>
| Isrc << $t = CdTextItem::CDTEXT_UPCEAN_ISRC; $lineNr = $1->getLine(); >>
| SizeInfo << $t = CdTextItem::CDTEXT_SIZE_INFO; $lineNr = $1->getLine(); >>
)
;
binaryData > [ const unsigned char *data, long len ]
: << static unsigned char buf[MAX_CD_TEXT_DATA_LEN];
$data = buf;
$len = 0;
int i;
int lineNr;
>>
"\{"
{ integer > [ i, lineNr ]
<< if (i < 0 || i > 255) {
log_message(-2, "%s:%d: Illegal binary data: %d", filename_, lineNr, i);
error_ = 1;
i = 0;
}
buf[0] = i;
$len = 1;
>>
( "," integer > [ i, lineNr ]
<< if (i < 0 || i > 255) {
log_message(-2, "%s:%d: Illegal binary data: %d",
filename_, lineNr, i);
error_ = 1;
i = 0;
}
if ($len >= MAX_CD_TEXT_DATA_LEN) {
log_message(-2, "%s:%d: Binary data exceeds maximum length (%d).",
filename_, lineNr, MAX_CD_TEXT_DATA_LEN);
error_ = 1;
}
else {
buf[$len] = i;
$len += 1;
}
>>
)*
}
"\}"
;
// fail action
<< $len = 0; >>
cdTextItem [ int blockNr ] > [ CdTextItem *item, int lineNr ]
: << $item = NULL;
CdTextItem::PackType type;
const char *s;
const unsigned char *data;
long len;
>>
packType > [ type, $lineNr ]
( stringEmpty > [ s ]
<< if (s != NULL) {
$item = new CdTextItem(type, blockNr, s);
delete[] s;
}
>>
| binaryData > [ data, len ]
<< $item = new CdTextItem(type, blockNr, data, len); >>
)
;
// fail action
<< delete $item;
$item = NULL;
>>
cdTextBlock [ CdTextContainer &container, int isTrack ]
: << CdTextItem *item = NULL;
int blockNr;
int lineNr;
>>
Language integer > [ blockNr, lineNr ]
"\{"
<< if (blockNr < 0 || blockNr > 7) {
log_message(-2, "%s:%d: Invalid block number, allowed range: [0..7].",
filename_, lineNr);
error_ = 1;
blockNr = 0;
}
>>
( cdTextItem [ blockNr ] > [ item, lineNr ]
<< if (item != NULL) {
int type = item->packType();
if (isTrack && ((type > 0x86 && type <= 0x89) || type == 0x8f)) {
log_message(-2, "%s:%d: Invalid CD-TEXT item for a track.",
filename_, lineNr);
error_ = 1;
delete item;
item = NULL;
}
else {
container.add(item);
item = NULL;
}
}
>>
)*
"\}"
;
// fail action
<< delete item; >>
cdTextLanguageMap [ CdTextContainer &container ]
: << int blockNr;
int lang;
int blockNrLine;
int langLine = 0;
>>
LanguageMap "\{"
( integer > [ blockNr, blockNrLine] ":"
( integer > [ lang, langLine ]
| LangEn << lang = 9; >>
)
<< if (blockNr >= 0 && blockNr <= 7) {
if (lang >= 0 && lang <= 255) {
container.language(blockNr, lang);
}
else {
log_message(-2,
"%s:%d: Invalid language code, allowed range: [0..255].",
filename_, langLine);
error_ = 1;
}
}
else {
log_message(-2,
"%s:%d: Invalid language number, allowed range: [0..7].",
filename_, blockNrLine);
error_ = 1;
}
>>
)+
"\}"
;
cdTextTrack [ CdTextContainer &container ]
:
CdText "\{"
( cdTextBlock [ container, 1 ] )*
"\}"
;
cdTextGlobal [ CdTextContainer &container ]
:
CdText "\{"
{ cdTextLanguageMap [ container ] }
( cdTextBlock [ container, 0 ] )*
"\}"
;
}
<<
ANTLRTokenType TocLexer::erraction()
{
log_message(-2, "%s:%d: Illegal token: %s", parser_->filename_,
_line, _lextext);
parser_->error_ = 1;
return Eof;
}
>>
<<
void TocParserGram::syn(_ANTLRTokenPtr tok, ANTLRChar *egroup,
SetWordType *eset, ANTLRTokenType etok, int k)
{
int line;
error_ = 1;
line = LT(1)->getLine();
log_message(-2, "%s:%d: syntax error at \"%s\" ", filename_, line,
LT(1)->getType() == Eof ? "EOF" : LT(1)->getText());
if ( !etok && !eset ) {
log_message(0, "");
return;
}
if ( k==1 ) {
log_message(0, "missing ");
}
else {
log_message(0, "; \"%s\" not ", LT(1)->getText());
if ( set_deg(eset)>1 ) log_message(-2, " in ");
}
if ( set_deg(eset)>0 )
edecode(eset);
else log_message(0, "%s ", token_tbl[etok]);
if ( strlen(egroup) > 0 )
log_message(0, "in %s ", egroup);
log_message(0, "");
}
Toc *parseToc(const char* inp, const char *filename)
{
DLGStringInput in(inp);
TocLexer scan(&in);
ANTLRTokenBuffer pipe(&scan);
ANTLRToken aToken;
scan.setToken(&aToken);
TocParserGram parser(&pipe);
parser.filename_ = filename;
scan.parser_ = &parser;
parser.init();
parser.error_ = 0;
Toc *t = parser.toc();
if (parser.error_ != 0) {
return NULL;
}
else {
return t;
}
}
Toc *parseToc(FILE *fp, const char *filename)
{
DLGFileInput in(fp);
TocLexer scan(&in);
ANTLRTokenBuffer pipe(&scan);
ANTLRToken aToken;
scan.setToken(&aToken);
TocParserGram parser(&pipe);
parser.filename_ = filename;
scan.parser_ = &parser;
parser.init();
parser.error_ = 0;
Toc *t = parser.toc();
if (parser.error_ != 0) {
return NULL;
}
else {
return t;
}
}
>>