Browse Source

Merged autotools to main trunk.

master
Svend Sorensen 19 years ago
parent
commit
51fe2af924
42 changed files with 3001 additions and 129 deletions
  1. +1
    -0
      AUTHORS
  2. +2
    -2
      COPYING
  3. +0
    -0
      ChangeLog
  4. +0
    -38
      Makefile
  5. +1
    -0
      Makefile.am
  6. +1
    -13
      NEWS
  7. +28
    -0
      README
  8. +0
    -34
      README.txt
  9. +0
    -7
      TODO
  10. +14
    -0
      configure.ac
  11. +2
    -0
      doc/Makefile.am
  12. +0
    -0
      doc/cuebreakpoints.1
  13. +0
    -0
      doc/cueconvert.1
  14. +0
    -0
      doc/cueprint.1
  15. +0
    -0
      extras/formats.txt
  16. +0
    -0
      extras/index.txt
  17. +0
    -35
      man/Makefile
  18. +39
    -0
      src/Makefile
  19. +1
    -0
      src/Makefile.am
  20. +7
    -0
      src/lib/Makefile.am
  21. +329
    -0
      src/lib/cd.c
  22. +161
    -0
      src/lib/cd.h
  23. +166
    -0
      src/lib/cdtext.c
  24. +71
    -0
      src/lib/cdtext.h
  25. +9
    -0
      src/lib/cue.h
  26. +280
    -0
      src/lib/cue_parse.y
  27. +44
    -0
      src/lib/cue_parse_prefix.h
  28. +147
    -0
      src/lib/cue_print.c
  29. +98
    -0
      src/lib/cue_scan.l
  30. +90
    -0
      src/lib/cuefile.c
  31. +16
    -0
      src/lib/cuefile.h
  32. +44
    -0
      src/lib/time.c
  33. +15
    -0
      src/lib/time.h
  34. +9
    -0
      src/lib/toc.h
  35. +346
    -0
      src/lib/toc_parse.y
  36. +44
    -0
      src/lib/toc_parse_prefix.h
  37. +149
    -0
      src/lib/toc_print.c
  38. +105
    -0
      src/lib/toc_scan.l
  39. +3
    -0
      src/tools/Makefile.am
  40. +163
    -0
      src/tools/cuebreakpoints.c
  41. +120
    -0
      src/tools/cueconvert.c
  42. +496
    -0
      src/tools/cueprint.c

+ 1
- 0
AUTHORS View File

@@ -0,0 +1 @@
Svend Sorensen <sorensen@users.berlios.de>

+ 2
- 2
COPYING View File

@@ -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.


+ 0
- 0
ChangeLog View File


+ 0
- 38
Makefile View File

@@ -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

+ 1
- 0
Makefile.am View File

@@ -0,0 +1 @@
SUBDIRS = doc src extras

CHANGES.txt → NEWS View File

@@ -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.

+ 28
- 0
README View File

@@ -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).

+ 0
- 34
README.txt View File

@@ -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.

BUGS.txt → TODO View File

@@ -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.

+ 14
- 0
configure.ac View File

@@ -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

+ 2
- 0
doc/Makefile.am View File

@@ -0,0 +1,2 @@
man_MANS = cuebreakpoints.1 cueconvert.1 cueprint.1
EXTRA_DIST = $(man_MANS)

man/cuebreakpoints.man → doc/cuebreakpoints.1 View File


man/cueconvert.man → doc/cueconvert.1 View File


man/cueprint.man → doc/cueprint.1 View File


formats.txt → extras/formats.txt View File


index.txt → extras/index.txt View File


+ 0
- 35
man/Makefile View File

@@ -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 $< > $@

+ 39
- 0
src/Makefile View File

@@ -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

+ 1
- 0
src/Makefile.am View File

@@ -0,0 +1 @@
SUBDIRS = lib tools

+ 7
- 0
src/lib/Makefile.am View File

@@ -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

+ 329
- 0
src/lib/cd.c View File

@@ -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]);
}
}

+ 161
- 0
src/lib/cd.h View File

@@ -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

+ 166
- 0
src/lib/cdtext.c View File

@@ -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);
}
}
}

+ 71
- 0
src/lib/cdtext.h View File

@@ -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

+ 9
- 0
src/lib/cue.h View File

@@ -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);

+ 280
- 0
src/lib/cue_parse.y View File

@@ -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;
}

+ 44
- 0
src/lib/cue_parse_prefix.h View File

@@ -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

+ 147
- 0
src/lib/cue_print.c View File

@@ -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));
}

+ 98
- 0
src/lib/cue_scan.l View File

@@ -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]); }

%%

+ 90
- 0
src/lib/cuefile.c View File

@@ -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;
}

+ 16
- 0
src/lib/cuefile.h View File

@@ -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);

+ 44
- 0
src/lib/time.c View File

@@ -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;
}

+ 15
- 0
src/lib/time.h View File

@@ -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

+ 9
- 0
src/lib/toc.h View File

@@ -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);

+ 346
- 0
src/lib/toc_parse.y View File

@@ -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;
}

+ 44
- 0
src/lib/toc_parse_prefix.h View File

@@ -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

+ 149
- 0
src/lib/toc_print.c View File

@@ -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);
}
}
}

+ 105
- 0
src/lib/toc_scan.l View File

@@ -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]); }

%%

+ 3
- 0
src/tools/Makefile.am View File

@@ -0,0 +1,3 @@
bin_PROGRAMS = cuebreakpoints cueconvert cueprint
LDADD = ../lib/libcuefile.a
AM_CPPFLAGS = -I$(srcdir)/../lib

+ 163
- 0
src/tools/cuebreakpoints.c View File

@@ -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;
}

+ 120
- 0
src/tools/cueconvert.c View File

@@ -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;
}

+ 496
- 0
src/tools/cueprint.c View File

@@ -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;
}

Loading…
Cancel
Save