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

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
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
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'.
This is free software, and you are welcome to redistribute it
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
-----------------

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

@@ -20,7 +11,6 @@ Changes since 1.2
* Programs exit if --input-format or --output-format is an illegal value.

Changes since 1.1
-----------------

* cuebreakpoints was not printing the last track breakpoint. This has been
fixed.
@@ -35,7 +25,6 @@ Changes since 1.1
* Added a track selection flag to cueprint.

Changes since 1.0
-----------------

* File formats documentation has been readded and updated (docs/formats.txt).

@@ -48,7 +37,6 @@ Changes since 1.0
expansion (like the printf command).

Changes since 0.6
-----------------

* cuetools-1.x is a significant code rewrite of 0.x. The cue/toc parsing code
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) Double quotes must be escaped in a string, otherwise
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