@@ -0,0 +1 @@ | |||||
Svend Sorensen <sorensen@users.berlios.de> |
@@ -2,7 +2,7 @@ | |||||
Version 2, June 1991 | Version 2, June 1991 | ||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc. | Copyright (C) 1989, 1991 Free Software Foundation, Inc. | ||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |||||
Everyone is permitted to copy and distribute verbatim copies | Everyone is permitted to copy and distribute verbatim copies | ||||
of this license document, but changing it is not allowed. | of this license document, but changing it is not allowed. | ||||
@@ -313,7 +313,7 @@ Also add information on how to contact you by electronic and paper mail. | |||||
If the program is interactive, make it output a short notice like this | If the program is interactive, make it output a short notice like this | ||||
when it starts in an interactive mode: | when it starts in an interactive mode: | ||||
Gnomovision version 69, Copyright (C) year name of author | |||||
Gnomovision version 69, Copyright (C) year name of author | |||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. | ||||
This is free software, and you are welcome to redistribute it | This is free software, and you are welcome to redistribute it | ||||
under certain conditions; type `show c' for details. | under certain conditions; type `show c' for details. | ||||
@@ -1,38 +0,0 @@ | |||||
SUBDIRS= lib tools man | |||||
all install uninstall: | |||||
for dir in $(SUBDIRS) ; do \ | |||||
(cd $$dir && $(MAKE) $@) ; \ | |||||
done | |||||
# reStructuredText Makefile | |||||
RST2HTML= rst2html.py | |||||
RST2LATEX= rst2latex.py | |||||
LATEX2PDF= pdflatex | |||||
RSTFILES= $(wildcard *.txt) | |||||
HTMLFILES= $(RSTFILES:.txt=.html) | |||||
LATEXFILES= $(RSTFILES:.txt=.tex) | |||||
PDFFILES= $(RSTFILES:.txt=.pdf) | |||||
html: $(HTMLFILES) | |||||
cd man && $(MAKE) $@ | |||||
latex: $(LATEXFILES) | |||||
pdf: $(PDFFILES) | |||||
%.html: %.txt | |||||
$(RST2HTML) $< > $@ | |||||
%.tex: %.txt | |||||
$(RST2LATEX) $< > $@ | |||||
%.pdf: %.tex | |||||
$(LATEX2PDF) $< | |||||
clean: | |||||
for dir in $(SUBDIRS) ; do \ | |||||
(cd $$dir && $(MAKE) $@) ; \ | |||||
done | |||||
rm -f $(HTMLFILES) $(LATEXFILES) $(PDFFILES) | |||||
rm -f *.aux *.log *.out |
@@ -0,0 +1 @@ | |||||
SUBDIRS = doc src extras |
@@ -1,15 +1,6 @@ | |||||
================ | |||||
cuetools CHANGES | |||||
================ | |||||
:Author: Svend Sorensen | |||||
:Contact: sorensen@users.berlios.de | |||||
Changes since 1.2 | Changes since 1.2 | ||||
----------------- | |||||
* prefix options have been removed from the lib/Makefile for Lex and Yacc (to | |||||
prepare for automake). | |||||
* Build system has been rewritten using automake/autoconf. | |||||
* Added append, prepend, and split pregap modes to cuebreakpoints. | * Added append, prepend, and split pregap modes to cuebreakpoints. | ||||
@@ -20,7 +11,6 @@ Changes since 1.2 | |||||
* Programs exit if --input-format or --output-format is an illegal value. | * Programs exit if --input-format or --output-format is an illegal value. | ||||
Changes since 1.1 | Changes since 1.1 | ||||
----------------- | |||||
* cuebreakpoints was not printing the last track breakpoint. This has been | * cuebreakpoints was not printing the last track breakpoint. This has been | ||||
fixed. | fixed. | ||||
@@ -35,7 +25,6 @@ Changes since 1.1 | |||||
* Added a track selection flag to cueprint. | * Added a track selection flag to cueprint. | ||||
Changes since 1.0 | Changes since 1.0 | ||||
----------------- | |||||
* File formats documentation has been readded and updated (docs/formats.txt). | * File formats documentation has been readded and updated (docs/formats.txt). | ||||
@@ -48,7 +37,6 @@ Changes since 1.0 | |||||
expansion (like the printf command). | expansion (like the printf command). | ||||
Changes since 0.6 | Changes since 0.6 | ||||
----------------- | |||||
* cuetools-1.x is a significant code rewrite of 0.x. The cue/toc parsing code | * cuetools-1.x is a significant code rewrite of 0.x. The cue/toc parsing code | ||||
has been reimplemented in lex/yacc. | has been reimplemented in lex/yacc. |
@@ -0,0 +1,28 @@ | |||||
cuetools - cue and toc file parsers and utilities | |||||
Copyright (C) 2004, 2005 Svend Sorensen | |||||
cuetools is a set of utilities for working with Cue Sheet (cue) and Table of | |||||
Contents (toc) files. | |||||
It includes: | |||||
cueconvert | |||||
convert between the cue and toc formats | |||||
cuebreakpoints | |||||
print the breakpoints from a cue or toc file | |||||
cueprint | |||||
print disc and track infomation for a cue or toc file | |||||
Directory layout: | |||||
doc/ | |||||
documentation (including man pages) | |||||
src/ | |||||
all source files | |||||
src/lib | |||||
scanning, parsing, and printing library | |||||
src/tools | |||||
cue and toc tools | |||||
Cuetools is hosted by BerliOS (http://cuetools.berlios.de). |
@@ -1,34 +0,0 @@ | |||||
=============== | |||||
cuetools README | |||||
=============== | |||||
:Author: Svend Sorensen | |||||
:Contact: sorensen@users.berlios.de | |||||
:Website: http://cuetools.berlios.de | |||||
Description | |||||
----------- | |||||
cuetools is a set of utilities for working with Cue Sheet (cue) and Table of | |||||
Contents (toc) files. | |||||
It includes: | |||||
cueconvert | |||||
convert between the cue and toc formats | |||||
cuebreakpoints | |||||
print the breakpoints from a cue or toc file | |||||
cueprint | |||||
print disc and track infomation for a cue or toc file | |||||
Installation | |||||
------------ | |||||
Building requires GNU Make, Lex, and Yacc. GNU Bison works in Yacc mode | |||||
(``make YACC="bison -y"``). | |||||
Run ``make install`` to install cuetools into the default location of | |||||
/usr/local. The Makefiles also support both the prefix and DESTDIR variables. |
@@ -1,10 +1,3 @@ | |||||
================== | |||||
cuetools Bugs/TODO | |||||
================== | |||||
:Author: Svend Sorensen | |||||
:Contact: sorensen@users.berlios.de | |||||
* (cue/toc) Line numbers in error messages are off sometimes. | * (cue/toc) Line numbers in error messages are off sometimes. | ||||
* (cue/toc) Double quotes must be escaped in a string, otherwise | * (cue/toc) Double quotes must be escaped in a string, otherwise | ||||
they will not be escaped when printed. | they will not be escaped when printed. |
@@ -0,0 +1,14 @@ | |||||
AC_INIT(cuetools,1.3pre,sorensen@users.berlios.de) | |||||
AM_INIT_AUTOMAKE($PACKAGE_NAME,$PACKAGE_VERSION) | |||||
# getting conflicting errors about config.h.in | |||||
#AM_CONFIG_HEADER(config.h) | |||||
#AC_CONFIG_HEADERS([config.h]) | |||||
AC_PROG_INSTALL | |||||
AC_PROG_RANLIB | |||||
AC_PROG_CC | |||||
AM_PROG_LEX | |||||
AC_PROG_YACC | |||||
AC_CONFIG_FILES([Makefile doc/Makefile src/Makefile src/lib/Makefile src/tools/Makefile extras/Makefile]) | |||||
AC_OUTPUT |
@@ -0,0 +1,2 @@ | |||||
man_MANS = cuebreakpoints.1 cueconvert.1 cueprint.1 | |||||
EXTRA_DIST = $(man_MANS) |
@@ -1,35 +0,0 @@ | |||||
# Makefile | |||||
INSTALL= install -c | |||||
INSTALL_PROGRAM= $(INSTALL) | |||||
INSTALL_DATA= $(INSTALL) -m 644 | |||||
GROFF= groff | |||||
prefix= /usr/local | |||||
mandir= $(prefix)/man | |||||
DESTDIR= | |||||
MANFILES= cuebreakpoints.man cueconvert.man cueprint.man | |||||
HTMLFILES= $(MANFILES:.man=.html) | |||||
html: $(HTMLFILES) | |||||
all: | |||||
@true | |||||
install: all | |||||
$(INSTALL) -d $(DESTDIR)$(mandir)/man1 | |||||
for i in $(MANFILES) ; do \ | |||||
$(INSTALL_DATA) $$i $(DESTDIR)$(mandir)/man1/`basename $$i .man`.1 ; \ | |||||
done | |||||
uninstall: | |||||
-for i in $(MANFILES) ; do \ | |||||
rm -f $(DESTDIR)$(mandir)/man1/`basename $$i .man`.1 ; \ | |||||
done | |||||
clean: | |||||
rm -f *.html | |||||
%.html: %.man | |||||
${GROFF} -man -T html $< > $@ |
@@ -0,0 +1,39 @@ | |||||
CPPFLAGS+= -I../lib | |||||
LDFLAGS+= -L../lib | |||||
LDLIBS+= -lcuefile | |||||
TARGETS= cuebreakpoints cueconvert cueprint | |||||
all: $(TARGETS) | |||||
clean: | |||||
rm -f $(TARGETS) | |||||
rm -f *.o | |||||
INSTALL= install -c | |||||
INSTALL_PROGRAM= $(INSTALL) | |||||
INSTALL_DATA= $(INSTALL) -m 644 | |||||
prefix= /usr/local | |||||
exec_prefix= $(prefix) | |||||
bindir= $(exec_prefix)/bin | |||||
DESTDIR= | |||||
install: all | |||||
$(INSTALL) -d $(DESTDIR)$(bindir) | |||||
for i in $(TARGETS) ; do \ | |||||
$(INSTALL_PROGRAM) $$i $(DESTDIR)$(bindir)/$$i ; \ | |||||
done | |||||
uninstall: | |||||
-for i in $(TARGETS) ; do \ | |||||
rm -f $(DESTDIR)$(bindir)/$$i ; \ | |||||
done | |||||
# dependancies | |||||
# generated by `gcc -MM *.c' | |||||
cuebreakpoints.o: cuebreakpoints.c ../lib/cuefile.h ../lib/cd.h \ | |||||
../lib/cdtext.h | |||||
cueconvert.o: cueconvert.c ../lib/cuefile.h ../lib/cd.h ../lib/cdtext.h | |||||
cueprint.o: cueprint.c ../lib/cuefile.h ../lib/cd.h ../lib/cdtext.h |
@@ -0,0 +1 @@ | |||||
SUBDIRS = lib tools |
@@ -0,0 +1,7 @@ | |||||
BUILT_SOURCES = cue_parse.h toc_parse.h | |||||
EXTRA_DIST = $(BUILT_SOURCES) | |||||
AM_YFLAGS = -d | |||||
AM_LFLAGS = -olex.yy.c | |||||
noinst_HEADERS = cd.h cdtext.h cue_parse_prefix.h toc_parse_prefix.h cuefile.h cue.h time.h toc.h | |||||
noinst_LIBRARIES = libcuefile.a | |||||
libcuefile_a_SOURCES = cd.c cdtext.c time.c cuefile.c cue_print.c toc_print.c cue_parse.y cue_scan.l toc_parse.y toc_scan.l |
@@ -0,0 +1,329 @@ | |||||
/* | |||||
* cd.c -- cd functions | |||||
* | |||||
* Copyright (C) 2004 Svend Sorensen | |||||
* For license terms, see the file COPYING in this distribution. | |||||
*/ | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include "cd.h" | |||||
typedef struct Data Data; | |||||
struct Data { | |||||
int type; /* DataType */ | |||||
char *name; /* data source name */ | |||||
long start; /* start time for data */ | |||||
long length; /* length of data */ | |||||
}; | |||||
struct Track { | |||||
Data zero_pre; /* pre-gap generated with zero data */ | |||||
Data file; /* track data file */ | |||||
Data zero_post; /* post-gap generated with zero data */ | |||||
int mode; /* track mode */ | |||||
int sub_mode; /* sub-channel mode */ | |||||
int flags; /* flags */ | |||||
char *isrc; /* IRSC Code (5.22.4) 12 bytes */ | |||||
Cdtext *cdtext; /* CD-TEXT */ | |||||
int nindex; /* number of indexes */ | |||||
long index[MAXINDEX]; /* indexes (in frames) (5.29.2.5) | |||||
* relative to start of track | |||||
* index[0] should always be zero */ | |||||
}; | |||||
struct Cd { | |||||
int mode; /* disc mode */ | |||||
char *catalog; /* Media Catalog Number (5.22.3) */ | |||||
Cdtext *cdtext; /* CD-TEXT */ | |||||
int ntrack; /* number of tracks in album */ | |||||
Track *track[MAXTRACK]; /* array of tracks */ | |||||
}; | |||||
Cd *cd_init () | |||||
{ | |||||
Cd *cd = NULL; | |||||
cd = malloc(sizeof(Cd)); | |||||
if(NULL == cd) { | |||||
fprintf(stderr, "unable to create cd\n"); | |||||
} else { | |||||
cd->mode = MODE_CD_DA; | |||||
cd->catalog = NULL; | |||||
cd->cdtext = cdtext_init(); | |||||
cd->ntrack = 0; | |||||
} | |||||
return cd; | |||||
} | |||||
Track *track_init () | |||||
{ | |||||
Track *track = NULL; | |||||
track = malloc(sizeof(Track)); | |||||
if (NULL == track) { | |||||
fprintf(stderr, "unable to create track\n"); | |||||
} else { | |||||
track->zero_pre.type = DATA_ZERO; | |||||
track->zero_pre.name = NULL; | |||||
track->zero_pre.start = 0; | |||||
track->zero_pre.length = 0; | |||||
track->file.type = DATA_AUDIO; | |||||
track->file.name = NULL; | |||||
track->file.start = 0; | |||||
track->file.length = 0; | |||||
track->zero_post.type = DATA_ZERO; | |||||
track->zero_post.name = NULL; | |||||
track->zero_post.start = 0; | |||||
track->zero_post.length = 0; | |||||
track->mode = MODE_AUDIO; | |||||
track->sub_mode = SUB_MODE_RW; | |||||
track->flags = FLAG_NONE; | |||||
track->isrc = NULL; | |||||
track->cdtext = cdtext_init(); | |||||
track->nindex = 0; | |||||
} | |||||
return track; | |||||
} | |||||
/* | |||||
* cd structure functions | |||||
*/ | |||||
void cd_set_mode (Cd *cd, int mode) | |||||
{ | |||||
cd->mode = mode; | |||||
} | |||||
int cd_get_mode (Cd *cd) | |||||
{ | |||||
return cd->mode; | |||||
} | |||||
void cd_set_catalog (Cd *cd, char *catalog) | |||||
{ | |||||
if (cd->catalog) | |||||
free(cd->catalog); | |||||
cd->catalog = strdup(catalog); | |||||
} | |||||
char *cd_get_catalog (Cd *cd) | |||||
{ | |||||
return cd->catalog; | |||||
} | |||||
Cdtext *cd_get_cdtext (Cd *cd) | |||||
{ | |||||
return cd->cdtext; | |||||
} | |||||
Track *cd_add_track (Cd *cd) | |||||
{ | |||||
if (MAXTRACK - 1 > cd->ntrack) | |||||
cd->ntrack++; | |||||
else | |||||
fprintf(stderr, "too many tracks\n"); | |||||
/* this will reinit last track if there were too many */ | |||||
cd->track[cd->ntrack - 1] = track_init(); | |||||
return cd->track[cd->ntrack - 1]; | |||||
} | |||||
int cd_get_ntrack (Cd *cd) | |||||
{ | |||||
return cd->ntrack; | |||||
} | |||||
Track *cd_get_track (Cd *cd, int i) | |||||
{ | |||||
if (0 < i <= cd->ntrack) | |||||
return cd->track[i - 1]; | |||||
return NULL; | |||||
} | |||||
/* | |||||
* track structure functions | |||||
*/ | |||||
void track_set_filename (Track *track, char *filename) | |||||
{ | |||||
if (track->file.name) | |||||
free(track->file.name); | |||||
track->file.name = strdup(filename); | |||||
} | |||||
char *track_get_filename (Track *track) | |||||
{ | |||||
return track->file.name; | |||||
} | |||||
void track_set_start (Track *track, long start) | |||||
{ | |||||
track->file.start = start; | |||||
} | |||||
long track_get_start (Track *track) | |||||
{ | |||||
return track->file.start; | |||||
} | |||||
void track_set_length (Track *track, long length) | |||||
{ | |||||
track->file.length = length; | |||||
} | |||||
long track_get_length (Track *track) | |||||
{ | |||||
return track->file.length; | |||||
} | |||||
void track_set_mode (Track *track, int mode) | |||||
{ | |||||
track->mode = mode; | |||||
} | |||||
int track_get_mode (Track *track) | |||||
{ | |||||
return track->mode; | |||||
} | |||||
void track_set_sub_mode (Track *track, int sub_mode) | |||||
{ | |||||
track->sub_mode = sub_mode; | |||||
} | |||||
int track_get_sub_mode (Track *track) | |||||
{ | |||||
return track->sub_mode; | |||||
} | |||||
void track_set_flag (Track *track, int flag) | |||||
{ | |||||
track->flags |= flag; | |||||
} | |||||
void track_clear_flag (Track *track, int flag) | |||||
{ | |||||
track->flags &= ~flag; | |||||
} | |||||
int track_is_set_flag (Track *track, int flag) | |||||
{ | |||||
return track->flags & flag; | |||||
} | |||||
void track_set_zero_pre (Track *track, long length) | |||||
{ | |||||
track->zero_pre.length = length; | |||||
} | |||||
long track_get_zero_pre (Track *track) | |||||
{ | |||||
return track->zero_pre.length; | |||||
} | |||||
void track_set_zero_post (Track *track, long length) | |||||
{ | |||||
track->zero_post.length = length; | |||||
} | |||||
long track_get_zero_post (Track *track) | |||||
{ | |||||
return track->zero_post.length; | |||||
} | |||||
void track_set_isrc (Track *track, char *isrc) | |||||
{ | |||||
if (track->isrc) | |||||
free(track->isrc); | |||||
track->isrc = strdup(isrc); | |||||
} | |||||
char *track_get_isrc (Track *track) | |||||
{ | |||||
return track->isrc; | |||||
} | |||||
Cdtext *track_get_cdtext (Track *track) | |||||
{ | |||||
return track->cdtext; | |||||
} | |||||
void track_add_index (Track *track, long index) | |||||
{ | |||||
if (MAXTRACK - 1 > track->nindex) | |||||
track->nindex++; | |||||
else | |||||
fprintf(stderr, "too many indexes\n"); | |||||
/* this will overwrite last index if there were too many */ | |||||
track->index[track->nindex - 1] = index; | |||||
} | |||||
int track_get_nindex (Track *track) | |||||
{ | |||||
return track->nindex; | |||||
} | |||||
long track_get_index (Track *track, int i) | |||||
{ | |||||
if (0 <= i < track->nindex) | |||||
return track->index[i]; | |||||
return -1; | |||||
} | |||||
/* | |||||
* dump cd information | |||||
*/ | |||||
void cd_track_dump (Track *track) | |||||
{ | |||||
int i; | |||||
printf("zero_pre: %ld\n", track->zero_pre.length); | |||||
printf("filename: %s\n", track->file.name); | |||||
printf("start: %ld\n", track->file.start); | |||||
printf("length: %ld\n", track->file.length); | |||||
printf("zero_post: %ld\n", track->zero_post.length); | |||||
printf("mode: %d\n", track->mode); | |||||
printf("sub_mode: %d\n", track->sub_mode); | |||||
printf("flags: 0x%x\n", track->flags); | |||||
printf("isrc: %s\n", track->isrc); | |||||
printf("indexes: %d\n", track->nindex); | |||||
for (i = 0; i < track->nindex; ++i) | |||||
printf("index %d: %ld\n", i, track->index[i]); | |||||
if (NULL != track->cdtext) { | |||||
printf("cdtext:\n"); | |||||
cdtext_dump(track->cdtext, 1); | |||||
} | |||||
} | |||||
void cd_dump (Cd *cd) | |||||
{ | |||||
int i; | |||||
printf("Disc Info\n"); | |||||
printf("mode: %d\n", cd->mode); | |||||
printf("catalog: %s\n", cd->catalog); | |||||
if (NULL != cd->cdtext) { | |||||
printf("cdtext:\n"); | |||||
cdtext_dump(cd->cdtext, 0); | |||||
} | |||||
for (i = 0; i < cd->ntrack; ++i) { | |||||
printf("Track %d Info\n", i + 1); | |||||
cd_track_dump(cd->track[i]); | |||||
} | |||||
} |
@@ -0,0 +1,161 @@ | |||||
/* | |||||
* cd.h -- cd structure | |||||
* | |||||
* Copyright (C) 2004 Svend Sorensen | |||||
* For license terms, see the file COPYING in this distribution. | |||||
*/ | |||||
/* references: MMC-3 draft revsion - 10g */ | |||||
#ifndef CD_H | |||||
#define CD_H | |||||
#include "cdtext.h" | |||||
#define MAXTRACK 99 /* Red Book track limit */ | |||||
#define MAXINDEX 99 /* Red Book index limit */ | |||||
/* | |||||
* disc modes | |||||
* DATA FORM OF MAIN DATA (5.29.2.8) | |||||
*/ | |||||
enum DiscMode { | |||||
MODE_CD_DA, /* CD-DA */ | |||||
MODE_CD_ROM, /* CD-ROM mode 1 */ | |||||
MODE_CD_ROM_XA /* CD-ROM XA and CD-I */ | |||||
}; | |||||
/* | |||||
* track modes | |||||
* 5.29.2.8 DATA FORM OF MAIN DATA | |||||
* Table 350 - Data Block Type Codes | |||||
*/ | |||||
enum TrackMode { | |||||
MODE_AUDIO, /* 2352 byte block length */ | |||||
MODE_MODE1, /* 2048 byte block length */ | |||||
MODE_MODE1_RAW, /* 2352 byte block length */ | |||||
MODE_MODE2, /* 2336 byte block length */ | |||||
MODE_MODE2_FORM1, /* 2048 byte block length */ | |||||
MODE_MODE2_FORM2, /* 2324 byte block length */ | |||||
MODE_MODE2_FORM_MIX, /* 2332 byte block length */ | |||||
MODE_MODE2_RAW /* 2352 byte block length */ | |||||
}; | |||||
/* | |||||
* sub-channel mode | |||||
* 5.29.2.13 Data Form of Sub-channel | |||||
* NOTE: not sure if this applies to cue files | |||||
*/ | |||||
enum TrackSubMode { | |||||
SUB_MODE_RW, /* RAW Data */ | |||||
SUB_MODE_RW_RAW /* PACK DATA (written R-W */ | |||||
}; | |||||
/* | |||||
* track flags | |||||
* Q Sub-channel Control Field (4.2.3.3, 5.29.2.2) | |||||
*/ | |||||
enum TrackFlag { | |||||
FLAG_NONE =0x00, /* no flags set */ | |||||
FLAG_PRE_EMPHASIS =0x01, /* audio recorded with pre-emphasis */ | |||||
FLAG_COPY_PERMITTED =0x02, /* digital copy permitted */ | |||||
FLAG_DATA =0x04, /* data track */ | |||||
FLAG_FOUR_CHANNEL =0x08, /* 4 audio channels */ | |||||
FLAG_SCMS =0x10, /* SCMS (not Q Sub-ch.) (5.29.2.7) */ | |||||
FLAG_ANY =0xff /* any flags set */ | |||||
}; | |||||
enum DataType { | |||||
DATA_AUDIO, | |||||
DATA_DATA, | |||||
DATA_FIFO, | |||||
DATA_ZERO | |||||
}; | |||||
/* ADTs */ | |||||
typedef struct Cd Cd; | |||||
typedef struct Track Track; | |||||
/* return pointer to CD structure */ | |||||
Cd *cd_init (); | |||||
/* dump all info from CD structure | |||||
* in human readable format (for debugging) | |||||
*/ | |||||
void cd_dump (Cd *cd); | |||||
/* | |||||
* Cd functions | |||||
*/ | |||||
void cd_set_mode (Cd *cd, int mode); | |||||
int cd_get_mode (Cd *cd); | |||||
void cd_set_catalog (Cd *cd, char *catalog); | |||||
char *cd_get_catalog (Cd *cd); | |||||
/* | |||||
* return pointer to cd's Cdtext | |||||
*/ | |||||
Cdtext *cd_get_cdtext (Cd *cd); | |||||
/* | |||||
* add a new track to cd, increment number of tracks | |||||
* and return pointer to new track | |||||
*/ | |||||
Track *cd_add_track (Cd *cd); | |||||
/* | |||||
* return number of tracks in cd | |||||
*/ | |||||
int cd_get_ntrack (Cd *cd); | |||||
Track *cd_get_track (Cd *cd, int i); | |||||
/* | |||||
* Track functions | |||||
*/ | |||||
/* filename of data file */ | |||||
void track_set_filename (Track *track, char *filename); | |||||
char *track_get_filename (Track *track); | |||||
/* track start is starting position in data file */ | |||||
void track_set_start (Track *track, long start); | |||||
long track_get_start (Track *track); | |||||
/* track length is length of data file to use */ | |||||
void track_set_length (Track *track, long length); | |||||
long track_get_length (Track *track); | |||||
/* see enum TrackMode */ | |||||
void track_set_mode (Track *track, int mode); | |||||
int track_get_mode (Track *track); | |||||
/* see enum TrackSubMode */ | |||||
void track_set_sub_mode (Track *track, int sub_mode); | |||||
int track_get_sub_mode (Track *track); | |||||
/* see enum TrackFlag */ | |||||
void track_set_flag (Track *track, int flag); | |||||
void track_clear_flag (Track *track, int flag); | |||||
int track_is_set_flag (Track *track, int flag); | |||||
/* zero data pregap */ | |||||
void track_set_zero_pre (Track *track, long length); | |||||
long track_get_zero_pre (Track *track); | |||||
/* zero data postgap */ | |||||
void track_set_zero_post (Track *track, long length); | |||||
long track_get_zero_post (Track *track); | |||||
void track_set_isrc (Track *track, char *isrc); | |||||
char *track_get_isrc (Track *track); | |||||
Cdtext *track_get_cdtext (Track *track); | |||||
void track_add_index (Track *track, long index); | |||||
int track_get_nindex (Track *track); | |||||
long track_get_index (Track *track, int i); | |||||
#endif |
@@ -0,0 +1,166 @@ | |||||
/* | |||||
* cdtext.c -- cdtext data structure and functions | |||||
* | |||||
* Copyright (C) 2004 Svend Sorensen | |||||
* For license terms, see the file COPYING in this distribution. | |||||
*/ | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include "cdtext.h" | |||||
struct Cdtext { | |||||
int pti; | |||||
int format; | |||||
char *value; | |||||
}; | |||||
Cdtext *cdtext_init () | |||||
{ | |||||
Cdtext *new_cdtext = NULL; | |||||
Cdtext cdtext[] = { | |||||
{PTI_TITLE, FORMAT_CHAR, NULL}, | |||||
{PTI_PERFORMER, FORMAT_CHAR, NULL}, | |||||
{PTI_SONGWRITER, FORMAT_CHAR, NULL}, | |||||
{PTI_COMPOSER, FORMAT_CHAR, NULL}, | |||||
{PTI_ARRANGER, FORMAT_CHAR, NULL}, | |||||
{PTI_MESSAGE, FORMAT_CHAR, NULL}, | |||||
{PTI_DISC_ID, FORMAT_BINARY, NULL}, | |||||
{PTI_GENRE, FORMAT_BINARY, NULL}, | |||||
{PTI_TOC_INFO1, FORMAT_BINARY, NULL}, | |||||
{PTI_TOC_INFO2, FORMAT_BINARY, NULL}, | |||||
{PTI_RESERVED1, FORMAT_CHAR, NULL}, | |||||
{PTI_RESERVED2, FORMAT_CHAR, NULL}, | |||||
{PTI_RESERVED3, FORMAT_CHAR, NULL}, | |||||
{PTI_RESERVED4, FORMAT_CHAR, NULL}, | |||||
{PTI_UPC_ISRC, FORMAT_CHAR, NULL}, | |||||
{PTI_SIZE_INFO, FORMAT_BINARY, NULL}, | |||||
{PTI_END, FORMAT_CHAR, NULL} | |||||
}; | |||||
new_cdtext = (Cdtext *) calloc (sizeof (cdtext) / sizeof (Cdtext), sizeof (Cdtext)); | |||||
if (NULL == new_cdtext) | |||||
fprintf (stderr, "problem allocating memory\n"); | |||||
else | |||||
memcpy (new_cdtext, cdtext, sizeof(cdtext)); | |||||
return new_cdtext; | |||||
} | |||||
void cdtext_delete (Cdtext *cdtext) | |||||
{ | |||||
int i; | |||||
if (NULL != cdtext) { | |||||
for (i = 0; PTI_END != cdtext[i].pti; i++) | |||||
free (cdtext[i].value); | |||||
free (cdtext); | |||||
} | |||||
} | |||||
/* return 0 if there is no cdtext, returns non-zero otherwise */ | |||||
int cdtext_is_empty (Cdtext *cdtext) | |||||
{ | |||||
for (; PTI_END != cdtext->pti; cdtext++) | |||||
if (NULL != cdtext->value) | |||||
return -1; | |||||
return 0; | |||||
} | |||||
/* sets cdtext's pti entry to field */ | |||||
void cdtext_set (int pti, char *value, Cdtext *cdtext) | |||||
{ | |||||
if (NULL != value) /* don't pass NULL to strdup */ | |||||
for (; PTI_END != cdtext->pti; cdtext++) | |||||
if (pti == cdtext->pti) { | |||||
free (cdtext->value); | |||||
cdtext->value = strdup (value); | |||||
} | |||||
} | |||||
/* returns value for pti, NULL if pti is not found */ | |||||
char *cdtext_get (int pti, Cdtext *cdtext) | |||||
{ | |||||
for (; PTI_END != cdtext->pti; cdtext++) | |||||
if (pti == cdtext->pti) | |||||
return cdtext->value; | |||||
return NULL; | |||||
} | |||||
const char *cdtext_get_key (int pti, int istrack) | |||||
{ | |||||
char *key = NULL; | |||||
switch (pti) { | |||||
case PTI_TITLE: | |||||
key = "TITLE"; | |||||
break; | |||||
case PTI_PERFORMER: | |||||
key = "PERFORMER"; | |||||
break; | |||||
case PTI_SONGWRITER: | |||||
key = "SONGWRITER"; | |||||
break; | |||||
case PTI_COMPOSER: | |||||
key = "COMPOSER"; | |||||
break; | |||||
case PTI_ARRANGER: | |||||
key = "ARRANGER"; | |||||
break; | |||||
case PTI_MESSAGE: | |||||
key = "MESSAGE"; | |||||
break; | |||||
case PTI_DISC_ID: | |||||
key = "DISC_ID"; | |||||
break; | |||||
case PTI_GENRE: | |||||
key = "GENRE"; | |||||
break; | |||||
case PTI_TOC_INFO1: | |||||
key = "TOC_INFO1"; | |||||
break; | |||||
case PTI_TOC_INFO2: | |||||
key = "TOC_INFO1"; | |||||
break; | |||||
case PTI_RESERVED1: | |||||
/* reserved */ | |||||
break; | |||||
case PTI_RESERVED2: | |||||
/* reserved */ | |||||
break; | |||||
case PTI_RESERVED3: | |||||
/* reserved */ | |||||
break; | |||||
case PTI_RESERVED4: | |||||
/* reserved */ | |||||
break; | |||||
case PTI_UPC_ISRC: | |||||
if (0 == istrack) | |||||
key = "UPC_EAN"; | |||||
else | |||||
key = "ISRC"; | |||||
break; | |||||
case PTI_SIZE_INFO: | |||||
key = "SIZE_INFO"; | |||||
break; | |||||
} | |||||
return key; | |||||
} | |||||
void cdtext_dump (Cdtext *cdtext, int istrack) | |||||
{ | |||||
int pti; | |||||
char *value = NULL; | |||||
for (pti = 0; PTI_END != pti; pti++) { | |||||
if (NULL != (value = cdtext_get(pti, cdtext))) { | |||||
printf("%s: ", cdtext_get_key(pti, istrack)); | |||||
printf("%s\n", value); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,71 @@ | |||||
/* | |||||
* cdtext.h | |||||
* | |||||
* Copyright (C) 2004 Svend Sorensen | |||||
* For license terms, see the file COPYING in this distribution. | |||||
*/ | |||||
/* references: MMC-3 draft revsion - 10g */ | |||||
#ifndef CDTEXT_H | |||||
#define CDTEXT_H | |||||
#include <stdio.h> | |||||
/* cdtext pack type indicators */ | |||||
enum Pti { | |||||
PTI_TITLE, /* title of album or track titles */ | |||||
PTI_PERFORMER, /* name(s) of the performer(s) */ | |||||
PTI_SONGWRITER, /* name(s) of the songwriter(s) */ | |||||
PTI_COMPOSER, /* name(s) of the composer(s) */ | |||||
PTI_ARRANGER, /* name(s) of the arranger(s) */ | |||||
PTI_MESSAGE, /* message(s) from the content provider and/or artist */ | |||||
PTI_DISC_ID, /* (binary) disc identification information */ | |||||
PTI_GENRE, /* (binary) genre identification and genre information */ | |||||
PTI_TOC_INFO1, /* (binary) table of contents information */ | |||||
PTI_TOC_INFO2, /* (binary) second table of contents information */ | |||||
PTI_RESERVED1, /* reserved */ | |||||
PTI_RESERVED2, /* reserved */ | |||||
PTI_RESERVED3, /* reserved */ | |||||
PTI_RESERVED4, /* reserved for content provider only */ | |||||
PTI_UPC_ISRC, /* UPC/EAN code of the album and ISRC code of each track */ | |||||
PTI_SIZE_INFO, /* (binary) size information of the block */ | |||||
PTI_END /* terminating PTI (for stepping through PTIs) */ | |||||
}; | |||||
enum PtiFormat { | |||||
FORMAT_CHAR, /* single or double byte character string */ | |||||
FORMAT_BINARY /* binary data */ | |||||
}; | |||||
typedef struct Cdtext Cdtext; | |||||
/* return a pointer to a new Cdtext */ | |||||
Cdtext *cdtext_init (); | |||||
/* release a Cdtext */ | |||||
void cdtext_delete (Cdtext *cdtext); | |||||
/* returns non-zero if there are no CD-TEXT fields set, zero otherwise */ | |||||
int cdtext_is_empty (Cdtext *cdtext); | |||||
/* set CD-TEXT field to value for PTI pti */ | |||||
void cdtext_set (int pti, char *value, Cdtext *cdtext); | |||||
/* returns pointer to CD-TEXT value for PTI pti */ | |||||
char *cdtext_get (int pti, Cdtext *cdtext); | |||||
/* | |||||
* returns appropriate string for PTI pti | |||||
* if istrack is zero, UPC/EAN string will be returned for PTI_UPC_ISRC | |||||
* othwise ISRC string will be returned | |||||
*/ | |||||
const char *cdtext_get_key (int pti, int istrack); | |||||
/* | |||||
* dump all cdtext info | |||||
* in human readable format (for debugging) | |||||
*/ | |||||
void cdtext_dump (Cdtext *cdtext, int istrack); | |||||
#endif |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
* cue.h -- cue function declarations | |||||
* | |||||
* Copyright (C) 2004 Svend Sorensen | |||||
* For license terms, see the file COPYING in this distribution. | |||||
*/ | |||||
Cd *cue_parse (FILE *fp); | |||||
void cue_print (FILE *fp, Cd *cd); |
@@ -0,0 +1,280 @@ | |||||
%{ | |||||
/* | |||||
* cue_parse.y -- parser for cue files | |||||
* | |||||
* Copyright (C) 2004 Svend Sorensen | |||||
* For license terms, see the file COPYING in this distribution. | |||||
*/ | |||||
#include <stdlib.h> | |||||
#include <stdio.h> | |||||
#include <string.h> | |||||
#include "cd.h" | |||||
#include "time.h" | |||||
#include "cue_parse_prefix.h" | |||||
#define YYDEBUG 1 | |||||
extern int yylex(); | |||||
void yyerror (char *s); | |||||
static Cd *cd = NULL; | |||||
static Track *track = NULL; | |||||
static Track *prev_track = NULL; | |||||
static Cdtext *cdtext = NULL; | |||||
static char *prev_filename = NULL; /* last file in or before last track */ | |||||
static char *cur_filename = NULL; /* last file in the last track */ | |||||
static char *new_filename = NULL; /* last file in this track */ | |||||
%} | |||||
%start cuefile | |||||
%union { | |||||
long ival; | |||||
char *sval; | |||||
} | |||||
%token <ival> NUMBER | |||||
%token <sval> STRING | |||||
/* global (header) */ | |||||
%token CATALOG | |||||
%token CDTEXTFILE | |||||
%token FFILE | |||||
%token BINARY | |||||
%token MOTOROLA | |||||
%token AIFF | |||||
%token WAVE | |||||
%token MP3 | |||||
/* track */ | |||||
%token TRACK | |||||
%token <ival> AUDIO | |||||
%token <ival> MODE1_2048 | |||||
%token <ival> MODE1_2352 | |||||
%token <ival> MODE2_2336 | |||||
%token <ival> MODE2_2048 | |||||
%token <ival> MODE2_2342 | |||||
%token <ival> MODE2_2332 | |||||
%token <ival> MODE2_2352 | |||||
/* ISRC is with CD_TEXT */ | |||||
%token TRACK_ISRC | |||||
%token FLAGS | |||||
%token <ival> PRE | |||||
%token <ival> DCP | |||||
%token <ival> FOUR_CH | |||||
%token <ival> SCMS | |||||
%token PREGAP | |||||
%token INDEX | |||||
%token POSTGAP | |||||
/* CD-TEXT */ | |||||
%token <ival> TITLE | |||||
%token <ival> PERFORMER | |||||
%token <ival> SONGWRITER | |||||
%token <ival> COMPOSER | |||||
%token <ival> ARRANGER | |||||
%token <ival> MESSAGE | |||||
%token <ival> DISC_ID | |||||
%token <ival> GENRE | |||||
%token <ival> TOC_INFO1 | |||||
%token <ival> TOC_INFO2 | |||||
%token <ival> UPC_EAN | |||||
%token <ival> ISRC | |||||
%token <ival> SIZE_INFO | |||||
%type <ival> track_mode | |||||
%type <ival> track_flag | |||||
%type <ival> time | |||||
%type <ival> cdtext_item | |||||
%% | |||||
cuefile | |||||
: new_cd global_statements track_list | |||||
; | |||||
new_cd | |||||
: /* empty */ { | |||||
cd = cd_init(); | |||||
cdtext = cd_get_cdtext(cd); | |||||
} | |||||
; | |||||
global_statements | |||||
: /* empty */ | |||||
| global_statements global_statement | |||||
; | |||||
global_statement | |||||
: CATALOG STRING '\n' { cd_set_catalog(cd, $2); } | |||||
| CDTEXTFILE STRING '\n' { /* ignored */ } | |||||
| cdtext | |||||
| track_data | |||||
| error '\n' | |||||
; | |||||
track_data | |||||
: FFILE STRING file_format '\n' { | |||||
if (NULL != new_filename) { | |||||
yyerror("too many files specified\n"); | |||||
free(new_filename); | |||||
} | |||||
new_filename = strdup($2); | |||||
} | |||||
; | |||||
track_list | |||||
: track | |||||
| track_list track | |||||
; | |||||
track | |||||
: new_track track_def track_statements | |||||
; | |||||
file_format | |||||
: BINARY | |||||
| MOTOROLA | |||||
| AIFF | |||||
| WAVE | |||||
| MP3 | |||||
; | |||||
new_track | |||||
: /*empty */ { | |||||
/* save previous track, to later set length */ | |||||
prev_track = track; | |||||
track = cd_add_track(cd); | |||||
cdtext = track_get_cdtext(track); | |||||
cur_filename = new_filename; | |||||
if (NULL != cur_filename) | |||||
prev_filename = cur_filename; | |||||
if (NULL == prev_filename) | |||||
yyerror("no file specified for track"); | |||||
else | |||||
track_set_filename(track, prev_filename); | |||||
new_filename = NULL; | |||||
} | |||||
; | |||||
track_def | |||||
: TRACK NUMBER track_mode '\n' { | |||||
track_set_mode(track, $3); | |||||
} | |||||
; | |||||
track_mode | |||||
: AUDIO | |||||
| MODE1_2048 | |||||
| MODE1_2352 | |||||
| MODE2_2336 | |||||
| MODE2_2048 | |||||
| MODE2_2342 | |||||
| MODE2_2332 | |||||
| MODE2_2352 | |||||
; | |||||
track_statements | |||||
: track_statement | |||||
| track_statements track_statement | |||||
; | |||||
track_statement | |||||
: cdtext | |||||
| FLAGS track_flags '\n' | |||||
| TRACK_ISRC STRING '\n' { track_set_isrc(track, $2); } | |||||
| PREGAP time '\n' { track_set_zero_pre(track, $2); } | |||||
| INDEX NUMBER time '\n' { | |||||
int i = track_get_nindex(track); | |||||
long prev_length; | |||||
if (0 == i) { | |||||
/* first index */ | |||||
track_set_start(track, $3); | |||||
if (NULL != prev_track && NULL == cur_filename) { | |||||
/* track shares file with previous track */ | |||||
prev_length = $3 - track_get_start(prev_track); | |||||
track_set_length(prev_track, prev_length); | |||||
} | |||||
} | |||||
for (; i <= $2; i++) | |||||
track_add_index(track, \ | |||||
track_get_zero_pre(track) + $3 \ | |||||
- track_get_start(track)); | |||||
} | |||||
| POSTGAP time '\n' { track_set_zero_post(track, $2); } | |||||
| track_data | |||||
| error '\n' | |||||
; | |||||
track_flags | |||||
: /* empty */ | |||||
| track_flags track_flag { track_set_flag(track, $2); } | |||||
; | |||||
track_flag | |||||
: PRE | |||||
| DCP | |||||
| FOUR_CH | |||||
| SCMS | |||||
; | |||||
cdtext | |||||
: cdtext_item STRING '\n' { cdtext_set ($1, $2, cdtext); } | |||||
; | |||||
cdtext_item | |||||
: TITLE | |||||
| PERFORMER | |||||
| SONGWRITER | |||||
| COMPOSER | |||||
| ARRANGER | |||||
| MESSAGE | |||||
| DISC_ID | |||||
| GENRE | |||||
| TOC_INFO1 | |||||
| TOC_INFO2 | |||||
| UPC_EAN | |||||
| ISRC | |||||
| SIZE_INFO | |||||
; | |||||
time | |||||
: NUMBER | |||||
| NUMBER ':' NUMBER ':' NUMBER { $$ = time_msf_to_frame($1, $3, $5); } | |||||
; | |||||
%% | |||||
/* lexer interface */ | |||||
extern int cue_lineno; | |||||
extern int yydebug; | |||||
extern FILE *cue_yyin; | |||||
void yyerror (char *s) | |||||
{ | |||||
fprintf(stderr, "%d: %s\n", cue_lineno, s); | |||||
} | |||||
Cd *cue_parse (FILE *fp) | |||||
{ | |||||
cue_yyin = fp; | |||||
yydebug = 0; | |||||
if (0 == yyparse()) | |||||
return cd; | |||||
return NULL; | |||||
} |
@@ -0,0 +1,44 @@ | |||||
/* Remap normal yacc names so we can have multiple parsers | |||||
* see http://www.gnu.org/software/automake/manual/html_node/Yacc-and-Lex.html | |||||
*/ | |||||
#define yymaxdepth cue_yymaxdepth | |||||
#define yyparse cue_yyparse | |||||
#define yylex cue_yylex | |||||
#define yyerror cue_yyerror | |||||
#define yylval cue_yylval | |||||
#define yychar cue_yychar | |||||
#define yydebug cue_yydebug | |||||
#define yypact cue_yypact | |||||
#define yyr1 cue_yyr1 | |||||
#define yyr2 cue_yyr2 | |||||
#define yydef cue_yydef | |||||
#define yychk cue_yychk | |||||
#define yypgo cue_yypgo | |||||
#define yyact cue_yyact | |||||
#define yyexca cue_yyexca | |||||
#define yyerrflag cue_yyerrflag | |||||
#define yynerrs cue_yynerrs | |||||
#define yyps cue_yyps | |||||
#define yypv cue_yypv | |||||
#define yys cue_yys | |||||
#define yy_yys cue_yy_yys | |||||
#define yystate cue_yystate | |||||
#define yytmp cue_yytmp | |||||
#define yyv cue_yyv | |||||
#define yy_yyv cue_yy_yyv | |||||
#define yyval cue_yyval | |||||
#define yylloc cue_yylloc | |||||
#define yyreds cue_yyreds | |||||
#define yytoks cue_yytoks | |||||
#define yylhs cue_yylhs | |||||
#define yylen cue_yylen | |||||
#define yydefred cue_yydefred | |||||
#define yydgoto cue_yydgoto | |||||
#define yysinde cue_yysindex | |||||
#define yyrindex cue_yyrindex | |||||
#define yygindex cue_yygindex | |||||
#define yytable cue_yytable | |||||
#define yycheck cue_yycheck | |||||
#define yyname cue_yyname | |||||
#define yyrule cue_yyrule |
@@ -0,0 +1,147 @@ | |||||
/* | |||||
* cue_print.y -- print cue file | |||||
* | |||||
* Copyright (C) 2004 Svend Sorensen | |||||
* For license terms, see the file COPYING in this distribution. | |||||
*/ | |||||
#include <stdio.h> | |||||
#include <string.h> | |||||
#include "cd.h" | |||||
#include "time.h" | |||||
void cue_print_track (FILE *fp, Track *track, int trackno); | |||||
void cue_print_cdtext (Cdtext *cdtext, FILE *fp, int istrack); | |||||
void cue_print_index (long i, FILE *fp); | |||||
char *filename = ""; /* last track datafile */ | |||||
long prev_length = 0; /* last track length */ | |||||
/* prints cd in cue format */ | |||||
void cue_print (FILE *fp, Cd *cd) | |||||
{ | |||||
Cdtext *cdtext = cd_get_cdtext(cd); | |||||
int i; /* track */ | |||||
Track *track = NULL; | |||||
/* print global information */ | |||||
if (NULL != cd_get_catalog(cd)) | |||||
fprintf(fp, "CATALOG %s\n", cd_get_catalog(cd)); | |||||
cue_print_cdtext(cdtext, fp, 0); | |||||
/* print track information */ | |||||
for (i = 1; i <= cd_get_ntrack(cd); i++) { | |||||
track = cd_get_track(cd, i); | |||||
fprintf(fp, "\n"); | |||||
cue_print_track(fp, track, i); | |||||
} | |||||
} | |||||
void cue_print_track (FILE *fp, Track *track, int trackno) | |||||
{ | |||||
Cdtext *cdtext = track_get_cdtext(track); | |||||
int i; /* index */ | |||||
if (NULL != track_get_filename(track)) { | |||||
/* | |||||
* always print filename for track 1, afterwards only | |||||
* print filename if it differs from the previous track | |||||
*/ | |||||
if (0 != strcmp(track_get_filename(track), filename)) { | |||||
filename = track_get_filename(track); | |||||
fprintf(fp, "FILE \"%s\" ", filename); | |||||
/* NOTE: what to do with other formats (MP3, etc)? */ | |||||
if (MODE_AUDIO == track_get_mode(track)) | |||||
fprintf(fp, "WAVE\n"); | |||||
else | |||||
fprintf(fp, "BINARY\n"); | |||||
} | |||||
} | |||||
fprintf(fp, "TRACK %02d ", trackno); | |||||
switch (track_get_mode(track)) { | |||||
case MODE_AUDIO: | |||||
fprintf(fp, "AUDIO\n"); | |||||
break; | |||||
case MODE_MODE1: | |||||
fprintf(fp, "MODE1/2048\n"); | |||||
break; | |||||
case MODE_MODE1_RAW: | |||||
fprintf(fp, "MODE1/2352\n"); | |||||
break; | |||||
case MODE_MODE2: | |||||
fprintf(fp, "MODE2/2048\n"); | |||||
break; | |||||
case MODE_MODE2_FORM1: | |||||
fprintf(fp, "MODE2/2336\n"); | |||||
break; | |||||
case MODE_MODE2_FORM2: | |||||
fprintf(fp, "MODE2/2324\n"); | |||||
break; | |||||
case MODE_MODE2_FORM_MIX: | |||||
fprintf(fp, "MODE2/2336\n"); | |||||
break; | |||||
case MODE_MODE2_RAW: | |||||
fprintf(fp, "MODE2/2352\n"); | |||||
break; | |||||
} | |||||
cue_print_cdtext(cdtext, fp, 1); | |||||
if (0 != track_is_set_flag(track, FLAG_ANY)) { | |||||
fprintf(fp, "FLAGS"); | |||||
if (0 != track_is_set_flag(track, FLAG_PRE_EMPHASIS)) | |||||
fprintf(fp, " PRE"); | |||||
if (0 != track_is_set_flag(track, FLAG_COPY_PERMITTED)) | |||||
fprintf(fp, " DCP"); | |||||
if (0 != track_is_set_flag(track, FLAG_FOUR_CHANNEL)) | |||||
fprintf(fp, " 4CH"); | |||||
if (0 != track_is_set_flag(track, FLAG_SCMS)) | |||||
fprintf(fp, " SCMS"); | |||||
fprintf(fp, "\n"); | |||||
} | |||||
if (NULL != track_get_isrc(track)) | |||||
fprintf(fp, "ISRC %s\n", track_get_isrc(track)); | |||||
if (0 != track_get_zero_pre(track)) | |||||
fprintf (fp, "PREGAP %s\n", time_frame_to_mmssff(track_get_zero_pre(track))); | |||||
/* don't print index 0 if index 1 = 0 */ | |||||
if (track_get_index(track, 1) == 0) | |||||
i = 1; | |||||
else | |||||
i = 0; | |||||
for (; i < track_get_nindex(track); i++) { | |||||
fprintf(fp, "INDEX %02d ", i); | |||||
cue_print_index( \ | |||||
track_get_index(track, i) \ | |||||
+ track_get_start(track) \ | |||||
- track_get_zero_pre(track) , fp); | |||||
} | |||||
if (0 != track_get_zero_post(track)) | |||||
fprintf (fp, "POSTGAP %s\n", time_frame_to_mmssff(track_get_zero_post(track))); | |||||
prev_length = track_get_length(track); | |||||
} | |||||
void cue_print_cdtext (Cdtext *cdtext, FILE *fp, int istrack) | |||||
{ | |||||
int pti; | |||||
char *value = NULL; | |||||
for (pti = 0; PTI_END != pti; pti++) { | |||||
if (NULL != (value = cdtext_get(pti, cdtext))) { | |||||
fprintf(fp, "%s", cdtext_get_key(pti, istrack)); | |||||
fprintf(fp, " \"%s\"\n", value); | |||||
} | |||||
} | |||||
} | |||||
void cue_print_index (long i, FILE *fp) | |||||
{ | |||||
fprintf (fp, "%s\n", time_frame_to_mmssff(i)); | |||||
} |
@@ -0,0 +1,98 @@ | |||||
%{ | |||||
/* | |||||
* cue_scan.l -- lexer for cue files | |||||
* | |||||
* Copyright (C) 2004 Svend Sorensen | |||||
* For license terms, see the file COPYING in this distribution. | |||||
*/ | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include "cd.h" | |||||
#include "cue_parse_prefix.h" | |||||
#include "cue_parse.h" | |||||
int cue_lineno = 1; | |||||
%} | |||||
ws [ \t\r] | |||||
nonws [^ \t\r\n] | |||||
%option noyywrap | |||||
%option prefix="cue_yy" | |||||
%s NAME | |||||
%% | |||||
\'([^\']|\\\')*\' | | |||||
\"([^\"]|\\\")*\" { | |||||
yylval.sval = strdup(yytext + 1); | |||||
yylval.sval[strlen(yylval.sval) - 1] = '\0'; | |||||
BEGIN(INITIAL); | |||||
return STRING; | |||||
} | |||||
<NAME>{nonws}+ { | |||||
yylval.sval = strdup(yytext); | |||||
BEGIN(INITIAL); | |||||
return STRING; | |||||
} | |||||
CATALOG { BEGIN(NAME); return CATALOG; } | |||||
CDTEXTFILE { BEGIN(NAME); return CDTEXTFILE; } | |||||
FILE { BEGIN(NAME); return FFILE; } | |||||
BINARY { return BINARY; } | |||||
MOTOROLA { return MOTOROLA; } | |||||
AIFF { return AIFF; } | |||||
WAVE { return WAVE; } | |||||
MP3 { return MP3; } | |||||
TRACK { return TRACK; } | |||||
AUDIO { yylval.ival = MODE_AUDIO; return AUDIO; } | |||||
MODE1\/2048 { yylval.ival = MODE_MODE1; return MODE1_2048; } | |||||
MODE1\/2352 { yylval.ival = MODE_MODE1_RAW; return MODE1_2352; } | |||||
MODE2\/2336 { yylval.ival = MODE_MODE2; return MODE2_2336; } | |||||
MODE2\/2048 { yylval.ival = MODE_MODE2_FORM1; return MODE2_2048; } | |||||
MODE2\/2342 { yylval.ival = MODE_MODE2_FORM2; return MODE2_2342; } | |||||
MODE2\/2332 { yylval.ival = MODE_MODE2_FORM_MIX; return MODE2_2332; } | |||||
MODE2\/2352 { yylval.ival = MODE_MODE2_RAW; return MODE2_2352; } | |||||
FLAGS { return FLAGS; } | |||||
PRE { yylval.ival = FLAG_PRE_EMPHASIS; return PRE; } | |||||
DCP { yylval.ival = FLAG_COPY_PERMITTED; return DCP; } | |||||
4CH { yylval.ival = FLAG_FOUR_CHANNEL; return FOUR_CH; } | |||||
SCMS { yylval.ival = FLAG_SCMS; return SCMS; } | |||||
PREGAP { return PREGAP; } | |||||
INDEX { return INDEX; } | |||||
POSTGAP { return POSTGAP; } | |||||
TITLE { BEGIN(NAME); yylval.ival = PTI_TITLE; return TITLE; } | |||||
PERFORMER { BEGIN(NAME); yylval.ival = PTI_PERFORMER; return PERFORMER; } | |||||
SONGWRITER { BEGIN(NAME); yylval.ival = PTI_SONGWRITER; return SONGWRITER; } | |||||
COMPOSER { BEGIN(NAME); yylval.ival = PTI_COMPOSER; return COMPOSER; } | |||||
ARRANGER { BEGIN(NAME); yylval.ival = PTI_ARRANGER; return ARRANGER; } | |||||
MESSAGE { BEGIN(NAME); yylval.ival = PTI_MESSAGE; return MESSAGE; } | |||||
DISC_ID { BEGIN(NAME); yylval.ival = PTI_DISC_ID; return DISC_ID; } | |||||
GENRE { BEGIN(NAME); yylval.ival = PTI_GENRE; return GENRE; } | |||||
TOC_INFO1 { BEGIN(NAME); yylval.ival = PTI_TOC_INFO1; return TOC_INFO1; } | |||||
TOC_INFO2 { BEGIN(NAME); yylval.ival = PTI_TOC_INFO2; return TOC_INFO2; } | |||||
UPC_EAN { BEGIN(NAME); yylval.ival = PTI_UPC_ISRC; return UPC_EAN; } | |||||
ISRC/{ws}+\" { BEGIN(NAME); yylval.ival = PTI_UPC_ISRC; return ISRC; } | |||||
SIZE_INFO { BEGIN(NAME); yylval.ival = PTI_SIZE_INFO; return SIZE_INFO; } | |||||
ISRC { BEGIN(NAME); return TRACK_ISRC; } | |||||
^{ws}*REM.*\n { cue_lineno++; /* ignore comments */ } | |||||
{ws}+ { /* ignore whitespace */ } | |||||
[[:digit:]]+ { yylval.ival = atoi(yytext); return NUMBER; } | |||||
: { return yytext[0]; } | |||||
^{ws}*\n { cue_lineno++; /* blank line */ } | |||||
\n { cue_lineno++; return '\n'; } | |||||
. { fprintf(stderr, "bad character '%c'\n", yytext[0]); } | |||||
%% |
@@ -0,0 +1,90 @@ | |||||
/* | |||||
* cuefile.c -- cue/toc functions | |||||
* | |||||
* Copyright (C) 2004 Svend Sorensen | |||||
* For license terms, see the file COPYING in this distribution. | |||||
*/ | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include "cuefile.h" | |||||
#include "cue.h" | |||||
#include "toc.h" | |||||
Cd *cf_parse (char *name, int *format) | |||||
{ | |||||
FILE *fp = NULL; | |||||
Cd *cd = NULL; | |||||
if (UNKNOWN == *format) | |||||
if (UNKNOWN == (*format = cf_format_from_suffix(name))) { | |||||
fprintf(stderr, "%s: unknown format\n", name); | |||||
return NULL; | |||||
} | |||||
if (0 == strcmp("-", name)) { | |||||
fp = stdin; | |||||
} else if (NULL == (fp = fopen(name, "r"))) { | |||||
fprintf(stderr, "%s: error opening file\n", name); | |||||
return NULL; | |||||
} | |||||
switch (*format) { | |||||
case CUE: | |||||
cd = cue_parse(fp); | |||||
break; | |||||
case TOC: | |||||
cd = toc_parse(fp); | |||||
break; | |||||
} | |||||
if(stdin != fp) | |||||
fclose(fp); | |||||
return cd; | |||||
} | |||||
int cf_print (char *name, int *format, Cd *cd) | |||||
{ | |||||
FILE *fp = NULL; | |||||
if (UNKNOWN == *format) | |||||
if (UNKNOWN == (*format = cf_format_from_suffix(name))) { | |||||
fprintf(stderr, "%s: unknown format\n", name); | |||||
return -1; | |||||
} | |||||
if (0 == strcmp("-", name)) { | |||||
fp = stdout; | |||||
} else if (NULL == (fp = fopen(name, "w"))) { | |||||
fprintf(stderr, "%s: error opening file\n", name); | |||||
return -1; | |||||
} | |||||
switch (*format) { | |||||
case CUE: | |||||
cue_print(fp, cd); | |||||
break; | |||||
case TOC: | |||||
toc_print(fp, cd); | |||||
break; | |||||
} | |||||
if(stdout != fp) | |||||
fclose(fp); | |||||
return 0; | |||||
} | |||||
int cf_format_from_suffix (char *name) | |||||
{ | |||||
char *suffix; | |||||
if (0 != (suffix = strrchr(name, '.'))) { | |||||
if (0 == strcasecmp(".cue", suffix)) | |||||
return CUE; | |||||
else if (0 == strcasecmp(".toc", suffix)) | |||||
return TOC; | |||||
} | |||||
return UNKNOWN; | |||||
} |
@@ -0,0 +1,16 @@ | |||||
/* | |||||
* cuefile.h -- cue/toc public declarations | |||||
* | |||||
* Copyright (C) 2004 Svend Sorensen | |||||
* For license terms, see the file COPYING in this distribution. | |||||
*/ | |||||
#include "cd.h" | |||||
enum {CUE, TOC, UNKNOWN}; | |||||
typedef struct Cue Cue; | |||||
Cd *cf_parse (char *fname, int *format); | |||||
int cf_print (char *fname, int *format, Cd *cue); | |||||
int cf_format_from_suffix (char *fname); |
@@ -0,0 +1,44 @@ | |||||
/* | |||||
* time.c -- time functions | |||||
* | |||||
* Copyright (C) 2004 Svend Sorensen | |||||
* For license terms, see the file COPYING in this distribution. | |||||
*/ | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
long time_msf_to_frame (int m, int s, int f) | |||||
{ | |||||
return (m * 60 + s) * 75 + f; | |||||
} | |||||
void msf_frame_to_msf (long frame, int *m, int *s, int *f) | |||||
{ | |||||
*f = frame % 75; /* 0 <= frames <= 74 */ | |||||
frame /= 75; | |||||
*s = frame % 60; /* 0 <= seconds <= 59 */ | |||||
frame /= 60; | |||||
*m = frame; /* 0 <= minutes */ | |||||
} | |||||
void time_frame_to_msf (long frame, int *m, int *s, int *f) | |||||
{ | |||||
*f = frame % 75; /* 0 <= frames <= 74 */ | |||||
frame /= 75; | |||||
*s = frame % 60; /* 0 <= seconds <= 59 */ | |||||
frame /= 60; | |||||
*m = frame; /* 0 <= minutes */ | |||||
} | |||||
/* print frame in mm:ss:ff format */ | |||||
char *time_frame_to_mmssff (long f) | |||||
{ | |||||
static char msf[9]; | |||||
int minutes, seconds, frames; | |||||
msf_frame_to_msf(f, &minutes, &seconds, &frames); | |||||
sprintf(msf, "%02d:%02d:%02d", minutes, seconds, frames); | |||||
return msf; | |||||
} |
@@ -0,0 +1,15 @@ | |||||
/* time.h -- time declarations | |||||
* | |||||
* Copyright (C) 2004 Svend Sorensen | |||||
* For license terms, see the file COPYING in this distribution. | |||||
*/ | |||||
#ifndef TIME_H | |||||
#define TIME_H | |||||
long time_msf_to_frame (int m, int s, int f); | |||||
long time_mmssff_to_frame (char *mmssff); | |||||
void time_frame_to_msf (long frame, int *m, int *s, int *f); | |||||
char *time_frame_to_mmssff (long f); | |||||
#endif |
@@ -0,0 +1,9 @@ | |||||
/* | |||||
* toc.h -- toc function declarations | |||||
* | |||||
* Copyright (C) 2004 Svend Sorensen | |||||
* For license terms, see the file COPYING in this distribution. | |||||
*/ | |||||
Cd *toc_parse (FILE *fp); | |||||
void toc_print (FILE *fp, Cd *cd); |
@@ -0,0 +1,346 @@ | |||||
%{ | |||||
/* | |||||
* toc_parse.y -- parser for toc files | |||||
* | |||||
* Copyright (C) 2004 Svend Sorensen | |||||
* For license terms, see the file COPYING in this distribution. | |||||
*/ | |||||
#include <stdlib.h> | |||||
#include <stdio.h> | |||||
#include <string.h> | |||||
#include "cd.h" | |||||
#include "time.h" | |||||
#include "toc_parse_prefix.h" | |||||
#define YYDEBUG 1 | |||||
extern int yylex(); | |||||
void yyerror (char *s); | |||||
static Cd *cd = NULL; | |||||
static Track *track = NULL; | |||||
static Cdtext *cdtext = NULL; | |||||
%} | |||||
%start tocfile | |||||
%union { | |||||
long ival; | |||||
char *sval; | |||||
} | |||||
%token <ival> NUMBER | |||||
%token <sval> STRING | |||||
/* global (header) */ | |||||
%token CATALOG | |||||
%token <ival> CD_DA | |||||
%token <ival> CD_ROM | |||||
%token <ival> CD_ROM_XA | |||||
/* track */ | |||||
%token TRACK | |||||
%token <ival> AUDIO | |||||
%token <ival> MODE1 | |||||
%token <ival> MODE1_RAW | |||||
%token <ival> MODE2 | |||||
%token <ival> MODE2_FORM1 | |||||
%token <ival> MODE2_FORM2 | |||||
%token <ival> MODE2_FORM_MIX | |||||
%token <ival> MODE2_RAW | |||||
%token <ival> RW | |||||
%token <ival> RW_RAW | |||||
%token NO | |||||
%token <ival> COPY | |||||
%token <ival> PRE_EMPHASIS | |||||
%token <ival> TWO_CHANNEL_AUDIO | |||||
%token <ival> FOUR_CHANNEL_AUDIO | |||||
%token ISRC | |||||
%token SILENCE | |||||
%token ZERO | |||||
%token AUDIOFILE | |||||
%token DATAFILE | |||||
%token FIFO | |||||
%token START | |||||
%token PREGAP | |||||
%token INDEX | |||||
/* CD-TEXT */ | |||||
%token CD_TEXT | |||||
%token LANGUAGE_MAP | |||||
%token LANGUAGE | |||||
%token <ival> TITLE | |||||
%token <ival> PERFORMER | |||||
%token <ival> SONGWRITER | |||||
%token <ival> COMPOSER | |||||
%token <ival> ARRANGER | |||||
%token <ival> MESSAGE | |||||
%token <ival> DISC_ID | |||||
%token <ival> GENRE | |||||
%token <ival> TOC_INFO1 | |||||
%token <ival> TOC_INFO2 | |||||
%token <ival> UPC_EAN | |||||
%token <ival> ISRC | |||||
%token <ival> SIZE_INFO | |||||
%type <ival> disc_mode | |||||
%type <ival> track_modes | |||||
%type <ival> track_mode | |||||
%type <ival> track_sub_mode | |||||
%type <ival> track_set_flag | |||||
%type <ival> track_clear_flag | |||||
%type <ival> time | |||||
%type <ival> cdtext_item | |||||
%% | |||||
tocfile | |||||
: new_cd global_statements track_list | |||||
; | |||||
new_cd | |||||
: /* empty */ { | |||||
cd = cd_init(); | |||||
cdtext = cd_get_cdtext(cd); | |||||
} | |||||
; | |||||
global_statements | |||||
: /* empty */ | |||||
| global_statements global_statement | |||||
; | |||||
global_statement | |||||
: CATALOG STRING '\n' { cd_set_catalog(cd, $2); } | |||||
| disc_mode '\n' { cd_set_mode(cd, $1); } | |||||
| CD_TEXT '{' opt_nl language_map cdtext_langs '}' '\n' | |||||
| error '\n' | |||||
; | |||||
disc_mode | |||||
: CD_DA | |||||
| CD_ROM | |||||
| CD_ROM_XA | |||||
; | |||||
track_list | |||||
: track | |||||
| track_list track | |||||
; | |||||
track | |||||
: new_track track_def track_statements { | |||||
while (2 > track_get_nindex(track)) | |||||
track_add_index(track, 0); | |||||
} | |||||
; | |||||
new_track | |||||
: /* empty */ { | |||||
track = cd_add_track(cd); | |||||
cdtext = track_get_cdtext(track); | |||||
/* add 0 index */ | |||||
track_add_index(track, 0); | |||||
} | |||||
; | |||||
track_def | |||||
: TRACK track_modes '\n' { track_set_mode(track, $2); } | |||||
; | |||||
track_modes | |||||
: track_mode | |||||
| track_mode track_sub_mode { track_set_sub_mode(track, $2); } | |||||
; | |||||
track_mode | |||||
: AUDIO | |||||
| MODE1 | |||||
| MODE1_RAW | |||||
| MODE2 | |||||
| MODE2_FORM1 | |||||
| MODE2_FORM2 | |||||
| MODE2_FORM_MIX | |||||
| MODE2_RAW | |||||
; | |||||
track_sub_mode | |||||
: RW | |||||
| RW_RAW | |||||
; | |||||
track_statements | |||||
: track_statement | |||||
| track_statements track_statement | |||||
; | |||||
track_statement | |||||
: track_flags | |||||
| ISRC STRING '\n' { track_set_isrc(track, $2); } | |||||
| CD_TEXT '{' opt_nl cdtext_langs '}' '\n' | |||||
| track_data | |||||
| track_pregap | |||||
| track_index | |||||
| error '\n' | |||||
; | |||||
track_flags | |||||
: track_set_flag { track_set_flag(track, $1); } | |||||
| track_clear_flag { track_clear_flag(track, $1); } | |||||
; | |||||
track_set_flag | |||||
: COPY '\n' | |||||
| PRE_EMPHASIS '\n' | |||||
| FOUR_CHANNEL_AUDIO '\n' | |||||
; | |||||
track_clear_flag | |||||
: NO PRE_EMPHASIS '\n' { $$ = $2; } | |||||
| NO COPY '\n' { $$ = $2; } | |||||
| TWO_CHANNEL_AUDIO '\n' | |||||
; | |||||
track_data | |||||
: zero_data time '\n' { | |||||
if (NULL == track_get_filename(track)) | |||||
track_set_zero_pre(track, $2); | |||||
else | |||||
track_set_zero_post(track, $2); | |||||
} | |||||
| AUDIOFILE STRING time '\n' { | |||||
track_set_filename(track, $2); | |||||
track_set_start(track, $3); | |||||
} | |||||
| AUDIOFILE STRING time time '\n' { | |||||
track_set_filename(track, $2); | |||||
track_set_start(track, $3); | |||||
track_set_length(track, $4); | |||||
} | |||||
| DATAFILE STRING '\n' { | |||||
track_set_filename(track, $2); | |||||
} | |||||
| DATAFILE STRING time '\n' { | |||||
track_set_filename(track, $2); | |||||
track_set_start(track, $3); | |||||
} | |||||
| FIFO STRING time '\n' { | |||||
track_set_filename(track, $2); | |||||
track_set_start(track, $3); | |||||
} | |||||
; | |||||
zero_data | |||||
: SILENCE | |||||
| ZERO | |||||
; | |||||
track_pregap | |||||
: START '\n' | |||||
| START time '\n' { | |||||
track_add_index(track, $2); | |||||
} | |||||
| PREGAP time '\n' { | |||||
track_set_zero_pre(track, $2); | |||||
track_add_index(track, $2); | |||||
} | |||||
; | |||||
track_index | |||||
: INDEX time '\n' { track_add_index(track, $2); } | |||||
; | |||||
language_map | |||||
: LANGUAGE_MAP '{' opt_nl languages '}' '\n' | |||||
; | |||||
languages | |||||
: language | |||||
| languages language | |||||
; | |||||
language | |||||
: NUMBER ':' NUMBER opt_nl { /* not implemented */ } | |||||
; | |||||
cdtext_langs | |||||
: cdtext_lang | |||||
| cdtext_langs cdtext_lang | |||||
; | |||||
cdtext_lang | |||||
: LANGUAGE NUMBER '{' opt_nl cdtext_defs '}' '\n' | |||||
; | |||||
cdtext_defs | |||||
: /* empty */ | |||||
| cdtext_defs cdtext_def | |||||
; | |||||
cdtext_def | |||||
: cdtext_item STRING '\n' { | |||||
cdtext_set ($1, $2, cdtext); | |||||
} | |||||
| cdtext_item '{' bytes '}' '\n' { | |||||
yyerror("binary CD-TEXT data not supported\n"); | |||||
} | |||||
; | |||||
cdtext_item | |||||
: TITLE | |||||
| PERFORMER | |||||
| SONGWRITER | |||||
| COMPOSER | |||||
| ARRANGER | |||||
| MESSAGE | |||||
| DISC_ID | |||||
| GENRE | |||||
| TOC_INFO1 | |||||
| TOC_INFO2 | |||||
| UPC_EAN | |||||
| ISRC | |||||
| SIZE_INFO | |||||
; | |||||
bytes | |||||
: /* empty */ | |||||
| bytes ',' NUMBER | |||||
; | |||||
time | |||||
: NUMBER | |||||
| NUMBER ':' NUMBER ':' NUMBER { $$ = time_msf_to_frame($1, $3, $5); } | |||||
; | |||||
opt_nl | |||||
: /* empty */ | |||||
| '\n' | |||||
; | |||||
%% | |||||
/* lexer interface */ | |||||
extern int toc_lineno; | |||||
extern int yydebug; | |||||
extern FILE *toc_yyin; | |||||
void yyerror (char *s) | |||||
{ | |||||
fprintf(stderr, "%d: %s\n", toc_lineno, s); | |||||
} | |||||
Cd *toc_parse (FILE *fp) | |||||
{ | |||||
toc_yyin = fp; | |||||
yydebug = 0; | |||||
if (0 == yyparse()) | |||||
return cd; | |||||
return NULL; | |||||
} |
@@ -0,0 +1,44 @@ | |||||
/* Remap normal yacc names so we can have multiple parsers | |||||
* see http://www.gnu.org/software/automake/manual/html_node/Yacc-and-Lex.html | |||||
*/ | |||||
#define yymaxdepth toc_yymaxdepth | |||||
#define yyparse toc_yyparse | |||||
#define yylex toc_yylex | |||||
#define yyerror toc_yyerror | |||||
#define yylval toc_yylval | |||||
#define yychar toc_ychar | |||||
#define yydebug toc_yydebug | |||||
#define yypact toc_yypact | |||||
#define yyr1 toc_yyr1 | |||||
#define yyr2 toc_yyr2 | |||||
#define yydef toc_yydef | |||||
#define yychk toc_yychk | |||||
#define yypgo toc_yypgo | |||||
#define yyact toc_yyact | |||||
#define yyexca toc_yyexca | |||||
#define yyerrflag toc_yyerrflag | |||||
#define yynerrs toc_yynerrs | |||||
#define yyps toc_yyps | |||||
#define yypv toc_yypv | |||||
#define yys toc_yys | |||||
#define yy_yys toc_yy_yys | |||||
#define yystate toc_yystate | |||||
#define yytmp toc_yytmp | |||||
#define yyv toc_yyv | |||||
#define yy_yyv toc_yy_yyv | |||||
#define yyval toc_yyval | |||||
#define yylloc toc_yylloc | |||||
#define yyreds toc_yyreds | |||||
#define yytoks toc_yytoks | |||||
#define yylhs toc_yylhs | |||||
#define yylen toc_yylen | |||||
#define yydefred toc_yydefred | |||||
#define yydgoto toc_yydgoto | |||||
#define yysinde toc_yysindex | |||||
#define yyrindex toc_yyrindex | |||||
#define yygindex toc_yygindex | |||||
#define yytable toc_yytable | |||||
#define yycheck toc_yycheck | |||||
#define yyname toc_yyname | |||||
#define yyrule toc_yyrule |
@@ -0,0 +1,149 @@ | |||||
/* | |||||
* toc_print.c -- print toc file | |||||
* | |||||
* Copyright (C) 2004 Svend Sorensen | |||||
* For license terms, see the file COPYING in this distribution. | |||||
*/ | |||||
#include <stdio.h> | |||||
#include <string.h> | |||||
#include "cd.h" | |||||
#include "time.h" | |||||
void toc_print_track (FILE *fp, Track *track); | |||||
void toc_print_cdtext (Cdtext *cdtext, FILE *fp, int istrack); | |||||
void toc_print (FILE *fp, Cd *cd) | |||||
{ | |||||
Cdtext *cdtext = cd_get_cdtext(cd); | |||||
int i; /* track */ | |||||
Track *track; | |||||
switch(cd_get_mode(cd)) { | |||||
case MODE_CD_DA: | |||||
fprintf(fp, "CD_DA\n"); | |||||
break; | |||||
case MODE_CD_ROM: | |||||
fprintf(fp, "CD_ROM\n"); | |||||
break; | |||||
case MODE_CD_ROM_XA: | |||||
fprintf(fp, "CD_ROM_XA\n"); | |||||
break; | |||||
} | |||||
if (NULL != cd_get_catalog(cd)) | |||||
fprintf(fp, "CATALOG \"%s\"\n", cd_get_catalog(cd)); | |||||
if(0 != cdtext_is_empty(cdtext)) { | |||||
fprintf(fp, "CD_TEXT {\n"); | |||||
fprintf(fp, "\tLANGUAGE_MAP { 0:9 }\n"); | |||||
fprintf(fp, "\tLANGUAGE 0 {\n"); | |||||
toc_print_cdtext(cdtext, fp, 0); | |||||
fprintf(fp, "\t}\n"); | |||||
fprintf(fp, "}\n"); | |||||
} | |||||
for (i = 1; i <= cd_get_ntrack(cd); i++) { | |||||
track = cd_get_track(cd, i); | |||||
fprintf(fp, "\n"); | |||||
toc_print_track(fp, track); | |||||
} | |||||
} | |||||
void toc_print_track (FILE *fp, Track *track) | |||||
{ | |||||
Cdtext *cdtext = track_get_cdtext(track); | |||||
int i; /* index */ | |||||
fprintf(fp, "TRACK "); | |||||
switch (track_get_mode(track)) { | |||||
case MODE_AUDIO: | |||||
fprintf(fp, "AUDIO"); | |||||
break; | |||||
case MODE_MODE1: | |||||
fprintf(fp, "MODE1"); | |||||
break; | |||||
case MODE_MODE1_RAW: | |||||
fprintf(fp, "MODE1_RAW"); | |||||
break; | |||||
case MODE_MODE2: | |||||
fprintf(fp, "MODE2"); | |||||
break; | |||||
case MODE_MODE2_FORM1: | |||||
fprintf(fp, "MODE2_FORM1"); | |||||
break; | |||||
case MODE_MODE2_FORM2: | |||||
fprintf(fp, "MODE2_FORM2"); | |||||
break; | |||||
case MODE_MODE2_FORM_MIX: | |||||
fprintf(fp, "MODE2_FORM_MIX"); | |||||
break; | |||||
} | |||||
fprintf(fp, "\n"); | |||||
if (0 != track_is_set_flag(track, FLAG_PRE_EMPHASIS)) | |||||
fprintf(fp, "PRE_EMPHASIS\n"); | |||||
if (0 != track_is_set_flag(track, FLAG_COPY_PERMITTED)) | |||||
fprintf(fp, "COPY\n"); | |||||
if (0 != track_is_set_flag(track, FLAG_FOUR_CHANNEL)) | |||||
fprintf(fp, "FOUR_CHANNEL_AUDIO\n"); | |||||
if (NULL != track_get_isrc(track)) | |||||
fprintf(fp, "ISRC \"%s\"\n", track_get_isrc(track)); | |||||
if (0 != cdtext_is_empty(cdtext)) { | |||||
fprintf(fp, "CD_TEXT {\n"); | |||||
fprintf(fp, "\tLANGUAGE 0 {\n"); | |||||
toc_print_cdtext(cdtext, fp, 1); | |||||
fprintf(fp, "\t}\n"); | |||||
fprintf(fp, "}\n"); | |||||
} | |||||
if (0 != track_get_zero_pre(track)) { | |||||
fprintf(fp, "ZERO "); | |||||
fprintf(fp, "%s", time_frame_to_mmssff(track_get_zero_pre(track))); | |||||
fprintf(fp, "\n"); | |||||
} | |||||
fprintf(fp, "FILE "); | |||||
fprintf(fp, "\"%s\" ", track_get_filename(track)); | |||||
if (0 == track_get_start(track)) | |||||
fprintf(fp, "0"); | |||||
else | |||||
fprintf(fp, "%s", time_frame_to_mmssff(track_get_start(track))); | |||||
if (0 != track_get_length(track)) | |||||
fprintf(fp, " %s", time_frame_to_mmssff(track_get_length(track))); | |||||
fprintf(fp, "\n"); | |||||
if (0 != track_get_zero_post(track)) { | |||||
fprintf(fp, "ZERO "); | |||||
fprintf(fp, "%s", time_frame_to_mmssff(track_get_zero_post(track))); | |||||
fprintf(fp, "\n"); | |||||
} | |||||
if (track_get_index(track, 1) != 0) { | |||||
fprintf(fp, "START "); | |||||
fprintf(fp, "%s\n", time_frame_to_mmssff(track_get_index(track, 1))); | |||||
} | |||||
for (i = 2; i < track_get_nindex(track); i++) { | |||||
fprintf(fp, "INDEX "); | |||||
fprintf(fp, "%s\n", time_frame_to_mmssff( \ | |||||
track_get_index(track, i) - track_get_index(track, 0) \ | |||||
)); | |||||
} | |||||
} | |||||
void toc_print_cdtext (Cdtext *cdtext, FILE *fp, int istrack) | |||||
{ | |||||
int pti; | |||||
char *value = NULL; | |||||
for (pti = 0; PTI_END != pti; pti++) { | |||||
if (NULL != (value = cdtext_get(pti, cdtext))) { | |||||
fprintf(fp, "\t\t"); | |||||
fprintf(fp, "%s", cdtext_get_key(pti, istrack)); | |||||
fprintf(fp, " \"%s\"\n", value); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,105 @@ | |||||
%{ | |||||
/* | |||||
* toc_scan.l -- lexer for toc files | |||||
* | |||||
* Copyright (C) 2004 Svend Sorensen | |||||
* For license terms, see the file COPYING in this distribution. | |||||
*/ | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include "cd.h" | |||||
#include "toc_parse_prefix.h" | |||||
#include "toc_parse.h" | |||||
int toc_lineno = 1; | |||||
%} | |||||
ws [ \t\r] | |||||
nonws [^ \t\r\n] | |||||
%option noyywrap | |||||
%option prefix="toc_yy" | |||||
%s NAME | |||||
%% | |||||
\'([^\']|\\\')*\' | | |||||
\"([^\"]|\\\")*\" { | |||||
yylval.sval = strdup(yytext + 1); | |||||
yylval.sval[strlen(yylval.sval) - 1] = '\0'; | |||||
BEGIN(INITIAL); | |||||
return STRING; | |||||
} | |||||
<NAME>{nonws}+ { | |||||
yylval.sval = strdup(yytext); | |||||
BEGIN(INITIAL); | |||||
return STRING; | |||||
} | |||||
CATALOG { BEGIN(NAME); return CATALOG; } | |||||
CD_DA { yylval.ival = MODE_CD_DA; return CD_DA; } | |||||
CD_ROM { yylval.ival = MODE_CD_ROM; return CD_ROM; } | |||||
CD_ROM_XA { yylval.ival = MODE_CD_ROM_XA; return CD_ROM_XA; } | |||||
TRACK { return TRACK; } | |||||
AUDIO { yylval.ival = MODE_AUDIO; return AUDIO; } | |||||
MODE1 { yylval.ival = MODE_MODE1; return MODE1; } | |||||
MODE1_RAW { yylval.ival = MODE_MODE1_RAW; return MODE1_RAW; } | |||||
MODE2 { yylval.ival = MODE_MODE2; return MODE2; } | |||||
MODE2_FORM1 { yylval.ival = MODE_MODE2_FORM1; return MODE2_FORM1; } | |||||
MODE2_FORM2 { yylval.ival = MODE_MODE2_FORM2; return MODE2_FORM2; } | |||||
MODE2_FORM_MIX { yylval.ival = MODE_MODE2_FORM_MIX; return MODE2_FORM_MIX; } | |||||
MODE2_RAW { yylval.ival = MODE_MODE2_RAW; return MODE2_RAW; } | |||||
RW { yylval.ival = SUB_MODE_RW; return RW; } | |||||
RW_RAW { yylval.ival = SUB_MODE_RW_RAW; return RW_RAW; } | |||||
NO { return NO; } | |||||
COPY { yylval.ival = FLAG_PRE_EMPHASIS; return COPY; } | |||||
PRE_EMPHASIS { yylval.ival = FLAG_COPY_PERMITTED; return PRE_EMPHASIS; } | |||||
FOUR_CHANNEL_AUDIO { yylval.ival = FLAG_FOUR_CHANNEL; return FOUR_CHANNEL_AUDIO; } | |||||
TWO_CHANNEL_AUDIO { yylval.ival = FLAG_FOUR_CHANNEL; return TWO_CHANNEL_AUDIO; } | |||||
/* ISRC is with CD-TEXT items */ | |||||
SILENCE { return SILENCE; } | |||||
ZERO { return ZERO; } | |||||
(AUDIO)?FILE { BEGIN(NAME); return AUDIOFILE; } | |||||
DATAFILE { BEGIN(NAME); return DATAFILE; } | |||||
FIFO { BEGIN(NAME); return FIFO; } | |||||
START { return START; } | |||||
PREGAP { return PREGAP; } | |||||
INDEX { return INDEX; } | |||||
CD_TEXT { return CD_TEXT; } | |||||
LANGUAGE_MAP { return LANGUAGE_MAP; } | |||||
LANGUAGE { return LANGUAGE; } | |||||
TITLE { BEGIN(NAME); yylval.ival = PTI_TITLE; return TITLE; } | |||||
PERFORMER { BEGIN(NAME); yylval.ival = PTI_PERFORMER; return PERFORMER; } | |||||
SONGWRITER { BEGIN(NAME); yylval.ival = PTI_SONGWRITER; return SONGWRITER; } | |||||
COMPOSER { BEGIN(NAME); yylval.ival = PTI_COMPOSER; return COMPOSER; } | |||||
ARRANGER { BEGIN(NAME); yylval.ival = PTI_ARRANGER; return ARRANGER; } | |||||
MESSAGE { BEGIN(NAME); yylval.ival = PTI_MESSAGE; return MESSAGE; } | |||||
DISC_ID { BEGIN(NAME); yylval.ival = PTI_DISC_ID; return DISC_ID; } | |||||
GENRE { BEGIN(NAME); yylval.ival = PTI_GENRE; return GENRE; } | |||||
TOC_INFO1 { BEGIN(NAME); yylval.ival = PTI_TOC_INFO1; return TOC_INFO1; } | |||||
TOC_INFO2 { BEGIN(NAME); yylval.ival = PTI_TOC_INFO2; return TOC_INFO2; } | |||||
UPC_EAN { BEGIN(NAME); yylval.ival = PTI_UPC_ISRC; return UPC_EAN; } | |||||
ISRC { BEGIN(NAME); yylval.ival = PTI_UPC_ISRC; return ISRC; } | |||||
SIZE_INFO { BEGIN(NAME); yylval.ival = PTI_SIZE_INFO; return SIZE_INFO; } | |||||
"//".*\n { toc_lineno++; /* ignore comments */ } | |||||
{ws}+ { /* ignore whitespace */ } | |||||
[[:digit:]]+ { yylval.ival = atoi(yytext); return NUMBER; } | |||||
:|,|\{|\} { return yytext[0]; } | |||||
^{ws}*\n { toc_lineno++; /* blank line */ } | |||||
\n { toc_lineno++; return '\n'; } | |||||
. { fprintf(stderr, "bad character '%c'\n", yytext[0]); } | |||||
%% |
@@ -0,0 +1,3 @@ | |||||
bin_PROGRAMS = cuebreakpoints cueconvert cueprint | |||||
LDADD = ../lib/libcuefile.a | |||||
AM_CPPFLAGS = -I$(srcdir)/../lib |
@@ -0,0 +1,163 @@ | |||||
/* | |||||
* cuebreakpoints.c -- print track break points | |||||
* | |||||
* Copyright (C) 2004 Svend Sorensen | |||||
* For license terms, see the file COPYING in this distribution. | |||||
*/ | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include <getopt.h> | |||||
#include "cuefile.h" | |||||
#include "time.h" | |||||
char *progname; | |||||
/* pregap correction modes | |||||
* APPEND - append pregap to previous track (except for first track) | |||||
* PREPEND - prefix pregap to current track | |||||
* SPLIT - print breakpoints for beginning and end of pregap | |||||
*/ | |||||
enum GapMode {APPEND, PREPEND, SPLIT}; | |||||
void usage (int status) | |||||
{ | |||||
if (0 == status) { | |||||
fprintf(stdout, "%s: usage: cuebreakpoints [option...] [file...]\n", progname); | |||||
fputs("\ | |||||
\n\ | |||||
OPTIONS\n\ | |||||
-h, --help print usage\n\ | |||||
-i, --input-format cue|toc set format of file(s)\n\ | |||||
--append-gaps append pregaps to previous track (default)\n\ | |||||
--prepend-gaps prefix pregaps to track\n\ | |||||
--split-gaps split at beginning and end of pregaps\n\ | |||||
", stdout); | |||||
} else { | |||||
fprintf(stderr, "run `%s --help' for usage\n", progname); | |||||
} | |||||
exit (status); | |||||
} | |||||
void print_m_ss_ff (long frame) | |||||
{ | |||||
int m, s, f; | |||||
time_frame_to_msf(frame, &m, &s, &f); | |||||
printf ("%d:%02d.%02d\n", m, s, f); | |||||
} | |||||
void print_breakpoint (long b) | |||||
{ | |||||
/* do not print zero breakpoints */ | |||||
if (0 != b) | |||||
print_m_ss_ff(b); | |||||
} | |||||
void print_breaks (Cd *cd, int gaps) | |||||
{ | |||||
int i; | |||||
long b; | |||||
Track *track; | |||||
for (i = 1; i <= cd_get_ntrack(cd); i++) { | |||||
track = cd_get_track(cd, i); | |||||
/* when breakpoint is at: | |||||
* index 0: gap is prepended to track | |||||
* index 1: gap is appended to previous track | |||||
*/ | |||||
b = track_get_start(track); | |||||
if (gaps == PREPEND || gaps == SPLIT) { | |||||
print_breakpoint(b); | |||||
} | |||||
if (gaps == APPEND || gaps == SPLIT) { | |||||
/* there is no previous track to append the first tracks pregap to */ | |||||
/* TODO: should first track's pregap be split when appending? | |||||
* this could be a suprising default | |||||
*/ | |||||
if (1 < i) { | |||||
b += track_get_index(track, 1) - track_get_zero_pre(track); | |||||
print_breakpoint(b); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
int breaks (char *name, int format, int gaps) | |||||
{ | |||||
Cd *cd = NULL; | |||||
if (NULL == (cd = cf_parse(name, &format))) { | |||||
fprintf(stderr, "%s: input file error\n", name); | |||||
return -1; | |||||
} | |||||
print_breaks(cd, gaps); | |||||
return 0; | |||||
} | |||||
int main (int argc, char **argv) | |||||
{ | |||||
int format = UNKNOWN; | |||||
int gaps = APPEND; | |||||
/* option variables */ | |||||
char c; | |||||
/* getopt_long() variables */ | |||||
extern char *optarg; | |||||
extern int optind; | |||||
static struct option longopts[] = { | |||||
{"help", no_argument, NULL, 'h'}, | |||||
{"input-format", required_argument, NULL, 'i'}, | |||||
{"append-gaps", no_argument, NULL, 'a'}, | |||||
{"prepend-gaps", no_argument, NULL, 'p'}, | |||||
{"split-gaps", no_argument, NULL, 's'}, | |||||
{NULL, 0, NULL, 0} | |||||
}; | |||||
progname = *argv; | |||||
while (-1 != (c = getopt_long(argc, argv, "hi:", longopts, NULL))) { | |||||
switch (c) { | |||||
case 'h': | |||||
usage(0); | |||||
break; | |||||
case 'i': | |||||
if (0 == strcmp("cue", optarg)) | |||||
format = CUE; | |||||
else if (0 == strcmp("toc", optarg)) | |||||
format = TOC; | |||||
else | |||||
fprintf(stderr, "%s: illegal format `%s'\n", progname, optarg); | |||||
usage(1); | |||||
break; | |||||
case 'a': | |||||
gaps = APPEND; | |||||
break; | |||||
case 'p': | |||||
gaps = PREPEND; | |||||
break; | |||||
case 's': | |||||
gaps = SPLIT; | |||||
break; | |||||
default: | |||||
usage(1); | |||||
break; | |||||
} | |||||
} | |||||
if (optind == argc) { | |||||
breaks("-", format, gaps); | |||||
} else { | |||||
for (; optind < argc; optind++) | |||||
breaks(argv[optind], format, gaps); | |||||
} | |||||
return 0; | |||||
} |
@@ -0,0 +1,120 @@ | |||||
/* | |||||
* cueconvert.c -- convert between cue/toc formats | |||||
* | |||||
* Copyright (C) 2004 Svend Sorensen | |||||
* For license terms, see the file COPYING in this distribution. | |||||
*/ | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include <getopt.h> | |||||
#include "cuefile.h" | |||||
char *progname; | |||||
void usage (int status) | |||||
{ | |||||
if (0 == status) { | |||||
fprintf(stdout, "%s: usage: cueconvert [option...] [infile [outfile]]\n", progname); | |||||
fputs("\ | |||||
\n\ | |||||
OPTIONS\n\ | |||||
-h, --help print usage\n\ | |||||
-i, --input-format cue|toc set format of input file\n\ | |||||
-o, --output-format cue|toc set format of output file\n\ | |||||
", stdout); | |||||
} else { | |||||
fprintf(stderr, "run `%s --help' for usage\n", progname); | |||||
} | |||||
exit (status); | |||||
} | |||||
int convert (char *iname, int iformat, char *oname, int oformat) | |||||
{ | |||||
Cd *cd = NULL; | |||||
if (NULL == (cd = cf_parse(iname, &iformat))) { | |||||
fprintf(stderr, "input file error\n"); | |||||
return -1; | |||||
} | |||||
if (UNKNOWN == oformat) { | |||||
/* first use file suffix */ | |||||
if (UNKNOWN == (oformat = cf_format_from_suffix(oname))) { | |||||
/* then use opposite of input format */ | |||||
switch(iformat) { | |||||
case CUE: | |||||
oformat = TOC; | |||||
break; | |||||
case TOC: | |||||
oformat = CUE; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
return cf_print(oname, &oformat, cd); | |||||
} | |||||
int main (int argc, char **argv) | |||||
{ | |||||
int iformat = UNKNOWN; | |||||
int oformat = UNKNOWN; | |||||
/* option variables */ | |||||
char c; | |||||
/* getopt_long() variables */ | |||||
extern char *optarg; | |||||
extern int optind; | |||||
static struct option longopts[] = { | |||||
{"help", no_argument, NULL, 'h'}, | |||||
{"input-format", required_argument, NULL, 'i'}, | |||||
{"output-format", required_argument, NULL, 'o'}, | |||||
{NULL, 0, NULL, 0} | |||||
}; | |||||
progname = *argv; | |||||
while (-1 != (c = getopt_long(argc, argv, "hi:o:", longopts, NULL))) { | |||||
switch (c) { | |||||
case 'h': | |||||
usage(0); | |||||
break; | |||||
case 'i': | |||||
if (0 == strcmp("cue", optarg)) | |||||
iformat = CUE; | |||||
else if (0 == strcmp("toc", optarg)) | |||||
iformat = TOC; | |||||
else | |||||
fprintf(stderr, "%s: illegal format `%s'\n", progname, optarg); | |||||
usage(1); | |||||
break; | |||||
case 'o': | |||||
if (0 == strcmp("cue", optarg)) | |||||
oformat = CUE; | |||||
else if (0 == strcmp("toc", optarg)) | |||||
oformat = TOC; | |||||
else | |||||
fprintf(stderr, "%s: illegal format `%s'\n", progname, optarg); | |||||
usage(1); | |||||
break; | |||||
default: | |||||
usage(1); | |||||
break; | |||||
} | |||||
} | |||||
if (optind == argc) { | |||||
convert("-", iformat, "-", oformat); | |||||
} else if (optind == argc - 1) { | |||||
convert(argv[optind], iformat, "-", oformat); | |||||
} else if (optind == argc - 2) { | |||||
convert(argv[optind], iformat, argv[optind + 1], oformat); | |||||
} else { | |||||
usage(1); | |||||
} | |||||
return 0; | |||||
} |
@@ -0,0 +1,496 @@ | |||||
/* | |||||
* cueprint.c -- print cd information based on a template | |||||
* | |||||
* Copyright (C) 2004 Svend Sorensen | |||||
* For license terms, see the file COPYING in this distribution. | |||||
*/ | |||||
#include <stdio.h> | |||||
#include <stdlib.h> /* exit() */ | |||||
#include <string.h> /* strcmp() */ | |||||
#include <getopt.h> | |||||
#include <ctype.h> /* isdigit() */ | |||||
#include "cuefile.h" | |||||
/* default templates */ | |||||
#define D_TEMPLATE "\ | |||||
Disc Information\n\ | |||||
arranger: %A\n\ | |||||
composer: %C\n\ | |||||
genre: %G\n\ | |||||
message: %M\n\ | |||||
no. of tracks: %N\n\ | |||||
performer: %P\n\ | |||||
songwriter: %S\n\ | |||||
title: %T\n\ | |||||
UPC/EAN: %U\n\ | |||||
" | |||||
#define T_TEMPLATE "\ | |||||
\n\ | |||||
Track %n Information\n\ | |||||
arranger: %a\n\ | |||||
composer: %c\n\ | |||||
genre: %g\n\ | |||||
ISRC: %i\n\ | |||||
message: %m\n\ | |||||
track number: %n\n\ | |||||
perfomer: %p\n\ | |||||
title: %t\n\ | |||||
ISRC (CD-TEXT): %u\n\ | |||||
" | |||||
/* default string to print for unset (NULL) values */ | |||||
#define VALUE_UNSET "" | |||||
/* | |||||
* *_get_* functions can return an int or char * | |||||
*/ | |||||
typedef union { | |||||
int ival; | |||||
char *sval; | |||||
char cval; | |||||
} Value; | |||||
char *progname; | |||||
void usage (int status) | |||||
{ | |||||
if (0 == status) { | |||||
fprintf(stdout, "%s: usage: cueprint [option...] [file...]\n", progname); | |||||
fputs("\ | |||||
\n\ | |||||
OPTIONS\n\ | |||||
-h, --help print usage\n\ | |||||
-i, --input-format cue|toc set format of file(s)\n\ | |||||
-n, --track-number <number> only print track information for single track\n\ | |||||
-d, --disc-template <template> set disc template (see TEMPLATE EXPANSION)\n\ | |||||
-t, --track-template <template> set track template (see TEMPLATE EXPANSION)\n\ | |||||
\n\ | |||||
Template Expansion\n\ | |||||
Disc:\n\ | |||||
%A - album arranger\n\ | |||||
%C - album composer\n\ | |||||
%G - album genre\n\ | |||||
%M - album message\n\ | |||||
%N - number of tracks\n\ | |||||
%P - album performer\n\ | |||||
%S - album songwriter\n\ | |||||
%T - album title\n\ | |||||
%U - album UPC/EAN\n\ | |||||
Track:\n\ | |||||
%a - track arranger\n\ | |||||
%c - track composer\n\ | |||||
%g - track genre\n\ | |||||
%i - track ISRC\n\ | |||||
%m - track message\n\ | |||||
%n - track number\n\ | |||||
%p - track perfomer\n\ | |||||
%t - track title\n\ | |||||
%u - track ISRC (CD-TEXT)\n\ | |||||
\n\ | |||||
Any other %<character> is expanded to that character. For example, to get a\n\ | |||||
'%', use %%.\n\ | |||||
\n\ | |||||
", stdout); | |||||
fprintf(stdout, "default disc template is:\n%s\n", D_TEMPLATE); | |||||
fprintf(stdout, "default track template is:\n%s\n", T_TEMPLATE); | |||||
} else { | |||||
fprintf(stderr, "run `%s --help' for usage\n", progname); | |||||
} | |||||
exit (status); | |||||
} | |||||
void disc_field (char *conv, int length, Cd *cd, Value *value) | |||||
{ | |||||
char *c; /* pointer to conversion character */ | |||||
Cdtext *cdtext = NULL; | |||||
cdtext = cd_get_cdtext(cd); | |||||
c = conv + length - 1; | |||||
/* | |||||
* after setting value, set *c to specify value type | |||||
* 'd' integer | |||||
* 's' string | |||||
* 'c' character | |||||
*/ | |||||
switch (*c) { | |||||
case 'A': | |||||
value->sval = cdtext_get(PTI_ARRANGER, cdtext); | |||||
*c = 's'; | |||||
break; | |||||
case 'C': | |||||
value->sval = cdtext_get(PTI_COMPOSER, cdtext); | |||||
*c = 's'; | |||||
break; | |||||
case 'G': | |||||
value->sval = cdtext_get(PTI_GENRE, cdtext); | |||||
*c = 's'; | |||||
break; | |||||
case 'M': | |||||
value->sval = cdtext_get(PTI_MESSAGE, cdtext); | |||||
*c = 's'; | |||||
break; | |||||
case 'N': | |||||
value->ival = cd_get_ntrack(cd); | |||||
*c = 'd'; | |||||
break; | |||||
case 'P': | |||||
value->sval = cdtext_get(PTI_PERFORMER, cdtext); | |||||
*c = 's'; | |||||
break; | |||||
case 'R': | |||||
value->sval = cdtext_get(PTI_ARRANGER, cdtext); | |||||
*c = 's'; | |||||
break; | |||||
case 'S': | |||||
value->sval = cdtext_get(PTI_SONGWRITER, cdtext); | |||||
*c = 's'; | |||||
break; | |||||
case 'T': | |||||
value->sval = cdtext_get(PTI_TITLE, cdtext); | |||||
*c = 's'; | |||||
break; | |||||
case 'U': | |||||
value->sval = cdtext_get(PTI_UPC_ISRC, cdtext); | |||||
*c = 's'; | |||||
break; | |||||
default: | |||||
value->cval = *c; | |||||
*c = 'c'; | |||||
break; | |||||
} | |||||
} | |||||
void track_field (char *conv, int length, Cd *cd, int trackno, Value *value) | |||||
{ | |||||
char *c; /* pointer to conversion character */ | |||||
Track *track = NULL; | |||||
Cdtext *cdtext = NULL; | |||||
track = cd_get_track(cd, trackno); | |||||
cdtext = track_get_cdtext(track); | |||||
c = conv + length - 1; | |||||
switch (*c) { | |||||
case 'a': | |||||
value->sval = cdtext_get(PTI_ARRANGER, cdtext); | |||||
*c = 's'; | |||||
break; | |||||
case 'c': | |||||
value->sval = cdtext_get(PTI_COMPOSER, cdtext); | |||||
*c = 's'; | |||||
break; | |||||
case 'f': | |||||
value->sval = track_get_filename(track); | |||||
*c = 's'; | |||||
break; | |||||
case 'g': | |||||
value->sval = cdtext_get(PTI_GENRE, cdtext); | |||||
*c = 's'; | |||||
break; | |||||
case 'i': | |||||
value->sval = track_get_isrc(track); | |||||
*c = 's'; | |||||
break; | |||||
case 'm': | |||||
value->sval = cdtext_get(PTI_MESSAGE, cdtext); | |||||
*c = 's'; | |||||
break; | |||||
case 'n': | |||||
value->ival = trackno; | |||||
*c = 'd'; | |||||
break; | |||||
case 'p': | |||||
value->sval = cdtext_get(PTI_PERFORMER, cdtext); | |||||
*c = 's'; | |||||
break; | |||||
case 's': | |||||
value->sval = cdtext_get(PTI_SONGWRITER, cdtext); | |||||
*c = 's'; | |||||
break; | |||||
case 't': | |||||
value->sval = cdtext_get(PTI_TITLE, cdtext); | |||||
*c = 's'; | |||||
break; | |||||
case 'u': | |||||
value->sval = cdtext_get(PTI_UPC_ISRC, cdtext); | |||||
*c = 's'; | |||||
break; | |||||
default: | |||||
disc_field(conv, length, cd, value); | |||||
break; | |||||
} | |||||
} | |||||
/* print a % conversion specification | |||||
* %[flag(s)][width][.precision]<conversion-char> | |||||
*/ | |||||
void print_conv (char *start, int length, Cd *cd, int trackno) | |||||
{ | |||||
char *conv; /* copy of conversion specification */ | |||||
Value value; | |||||
char *c; /* pointer to conversion-char */ | |||||
/* TODO: use strndup? */ | |||||
conv = malloc ((unsigned) (length + 1)); | |||||
strncpy(conv, start, length); | |||||
conv[length] = '\0'; | |||||
/* conversion character */ | |||||
if (0 == trackno) | |||||
disc_field(conv, length, cd, &value); | |||||
else | |||||
track_field(conv, length, cd, trackno, &value); | |||||
c = conv + length - 1; | |||||
switch (*c) { | |||||
case 'c': | |||||
printf(conv, value.cval); | |||||
break; | |||||
case 'd': | |||||
printf(conv, value.ival); | |||||
break; | |||||
case 's': | |||||
if (NULL == value.sval) | |||||
printf(conv, VALUE_UNSET); | |||||
else | |||||
printf(conv, value.sval); | |||||
break; | |||||
default: | |||||
printf("%d: ", strlen(conv)); | |||||
printf("%s", conv); | |||||
} | |||||
free(conv); | |||||
} | |||||
void cd_printf (char *format, Cd *cd, int trackno) | |||||
{ | |||||
char *c; /* pointer into format */ | |||||
char *conv_start; | |||||
int conv_length; | |||||
for (c = format; '\0' != *c; c++) { | |||||
if ('%' == *c) { | |||||
conv_start = c; | |||||
conv_length = 1; | |||||
c++; | |||||
/* flags */ | |||||
while ( \ | |||||
'-' == *c \ | |||||
|| '+' == *c \ | |||||
|| ' ' == *c \ | |||||
|| '0' == *c \ | |||||
|| '#' == *c \ | |||||
) { | |||||
conv_length++; | |||||
c++; | |||||
} | |||||
/* field width */ | |||||
/* '*' not recognized */ | |||||
while (0 != isdigit(*c)) { | |||||
conv_length++; | |||||
c++; | |||||
} | |||||
/* precision */ | |||||
/* '*' not recognized */ | |||||
if ('.' == *c) { | |||||
conv_length++; | |||||
c++; | |||||
while (0 != isdigit(*c)) { | |||||
conv_length++; | |||||
c++; | |||||
} | |||||
} | |||||
/* length modifier (h, l, or L) */ | |||||
/* not recognized */ | |||||
/* conversion character */ | |||||
conv_length++; | |||||
print_conv(conv_start, conv_length, cd, trackno); | |||||
} else { | |||||
putchar(*c); | |||||
} | |||||
} | |||||
} | |||||
int info (char *name, int format, int trackno, char *d_template, char *t_template) | |||||
{ | |||||
Cd *cd = NULL; | |||||
int ntrack; | |||||
if (NULL == (cd = cf_parse(name, &format))) { | |||||
fprintf(stderr, "%s: input file error\n", name); | |||||
return -1; | |||||
} | |||||
ntrack = cd_get_ntrack(cd); | |||||
if (-1 == trackno) { | |||||
cd_printf(d_template, cd, 0); | |||||
for (trackno = 1; trackno <= ntrack; trackno++) { | |||||
cd_printf(t_template, cd, trackno); | |||||
} | |||||
} else if (0 == trackno) { | |||||
cd_printf(d_template, cd, trackno); | |||||
} else if (0 < trackno && ntrack >= trackno) { | |||||
cd_printf(t_template, cd, trackno); | |||||
} else { | |||||
fprintf(stderr, "%s: track number out of range\n", progname); | |||||
return -1; | |||||
} | |||||
return 0; | |||||
} | |||||
/* translate escape sequences in a string | |||||
* string is overwritten and terminated | |||||
* TODO: this does not handle octal and hexidecimal escapes | |||||
* except for \0 | |||||
*/ | |||||
void translate_escapes(char *s) | |||||
{ | |||||
char *read; | |||||
char *write; | |||||
read = s; | |||||
write = s; | |||||
while ('\0' != *read) { | |||||
if ('\\' == *read) { | |||||
read++; | |||||
switch (*read) { | |||||
case 'a': | |||||
*write = '\a'; | |||||
break; | |||||
case 'b': | |||||
*write = '\b'; | |||||
break; | |||||
case 'f': | |||||
*write = '\f'; | |||||
break; | |||||
case 'n': | |||||
*write = '\n'; | |||||
break; | |||||
case 'r': | |||||
*write = '\r'; | |||||
break; | |||||
case 't': | |||||
*write = '\t'; | |||||
break; | |||||
case 'v': | |||||
*write = '\v'; | |||||
break; | |||||
case '0': | |||||
*write = '\0'; | |||||
break; | |||||
default: | |||||
/* ?, ', " are handled by the default */ | |||||
*write = *read; | |||||
break; | |||||
} | |||||
} else { | |||||
*write = *read; | |||||
} | |||||
read++; | |||||
write++; | |||||
} | |||||
*write = '\0'; | |||||
} | |||||
int main (int argc, char **argv) | |||||
{ | |||||
int format = UNKNOWN; | |||||
int trackno = -1; /* track number (-1 = unspecified, 0 = disc info) */ | |||||
char *d_template = NULL; /* disc template */ | |||||
char *t_template = NULL; /* track template */ | |||||
/* getopt_long() variables */ | |||||
char c; | |||||
extern char *optarg; | |||||
extern int optind; | |||||
static struct option longopts[] = { | |||||
{"help", no_argument, NULL, 'h'}, | |||||
{"input-format", required_argument, NULL, 'i'}, | |||||
{"track-number", required_argument, NULL, 'n'}, | |||||
{"disc-template", required_argument, NULL, 'd'}, | |||||
{"track-template", required_argument, NULL, 't'}, | |||||
{NULL, 0, NULL, 0} | |||||
}; | |||||
progname = *argv; | |||||
while (-1 != (c = getopt_long(argc, argv, "hi:n:d:t:", longopts, NULL))) { | |||||
switch (c) { | |||||
case 'h': | |||||
usage(0); | |||||
break; | |||||
case 'i': | |||||
if (0 == strcmp("cue", optarg)) | |||||
format = CUE; | |||||
else if (0 == strcmp("toc", optarg)) | |||||
format = TOC; | |||||
else | |||||
fprintf(stderr, "%s: illegal format `%s'\n", progname, optarg); | |||||
usage(1); | |||||
break; | |||||
case 'n': | |||||
trackno = atoi(optarg); | |||||
break; | |||||
case 'd': | |||||
d_template = optarg; | |||||
break; | |||||
case 't': | |||||
t_template = optarg; | |||||
break; | |||||
default: | |||||
usage(1); | |||||
break; | |||||
} | |||||
} | |||||
/* if no disc or track template is set, use the defaults for both */ | |||||
/* TODO: alternative to strdup to get variable strings? */ | |||||
if (NULL == d_template && NULL == t_template) { | |||||
d_template = strdup(D_TEMPLATE); | |||||
t_template = strdup(T_TEMPLATE); | |||||
} else { | |||||
if (NULL == d_template) | |||||
d_template = strdup(""); | |||||
if (NULL == t_template) | |||||
t_template = strdup(""); | |||||
} | |||||
/* translate escape sequences */ | |||||
translate_escapes(d_template); | |||||
translate_escapes(t_template); | |||||
if (optind == argc) { | |||||
info("-", format, trackno, d_template, t_template); | |||||
} else { | |||||
for (; optind < argc; optind++) | |||||
info(argv[optind], format, trackno, d_template, t_template); | |||||
} | |||||
return 0; | |||||
} |