From 40897d39642f7942f1e46d2eb6c30fd77341fe61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Tue, 15 May 2007 00:51:35 +0000 Subject: [PATCH] * Moved cdda_text test application to the test directory, and separated it from cdda.cpp. * Implemented most of the directory retrieval functions of the file system; even renaming titles should work now. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21139 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/kernel/file_systems/cdda/Jamfile | 5 +- src/add-ons/kernel/file_systems/cdda/cdda.cpp | 313 +++++++++--------- src/add-ons/kernel/file_systems/cdda/cdda.h | 34 ++ .../file_systems/cdda/kernel_interface.cpp | 126 ++++++- src/tests/add-ons/kernel/file_systems/Jamfile | 1 + .../add-ons/kernel/file_systems/cdda/Jamfile | 9 + .../kernel/file_systems/cdda/cdda_text.cpp | 48 +++ 7 files changed, 364 insertions(+), 172 deletions(-) create mode 100644 src/add-ons/kernel/file_systems/cdda/cdda.h create mode 100644 src/tests/add-ons/kernel/file_systems/cdda/Jamfile create mode 100644 src/tests/add-ons/kernel/file_systems/cdda/cdda_text.cpp diff --git a/src/add-ons/kernel/file_systems/cdda/Jamfile b/src/add-ons/kernel/file_systems/cdda/Jamfile index a938338c90..6eb8fc843b 100644 --- a/src/add-ons/kernel/file_systems/cdda/Jamfile +++ b/src/add-ons/kernel/file_systems/cdda/Jamfile @@ -6,8 +6,5 @@ UsePrivateHeaders [ FDirName storage ] ; KernelAddon cdda : kernel_interface.cpp - ; - -SimpleTest cdda_text : cdda.cpp - : be ; + ; diff --git a/src/add-ons/kernel/file_systems/cdda/cdda.cpp b/src/add-ons/kernel/file_systems/cdda/cdda.cpp index 0ca8776fc6..5d50ca55b9 100644 --- a/src/add-ons/kernel/file_systems/cdda/cdda.cpp +++ b/src/add-ons/kernel/file_systems/cdda/cdda.cpp @@ -4,17 +4,15 @@ */ -#include +#include "cdda.h" + +#include #include #include -#include #include #include -#include -#define kFramesPerSecond 75 -#define kFramesPerMinute (kFramesPerSecond * 60) struct cdtext_pack_data { uint8 id; @@ -27,35 +25,37 @@ struct cdtext_pack_data { uint8 crc[2]; } _PACKED; - -static const uint8 kMaxTracks = 0x63; enum { kTrackID = 0x80, kArtistID = 0x81, kMessageID = 0x85, }; -struct cdtext { - char *artist; - char *album; - char *titles[kMaxTracks]; - char *artists[kMaxTracks]; - uint8 track_count; - char *genre; -}; +static const uint32 kBufferSize = 16384; +static const uint32 kSenseSize = 1024; // #pragma mark - string functions -bool +static char * +copy_string(const char *string) +{ + if (string == NULL || !string[0]) + return NULL; + + return strdup(string); +} + + +static bool is_garbage(char c) { return isspace(c) || c == '-' || c == '/' || c == '\\'; } -void +static void sanitize_string(char *string) { if (string == NULL) @@ -106,7 +106,7 @@ find_string(const char *string, const char *find) } -void +static void cut_string(char *string, char *cut) { if (string == NULL || cut == NULL) @@ -121,7 +121,7 @@ cut_string(char *string, char *cut) } -void +static void sanitize_album(cdtext &text) { cut_string(text.album, text.artist); @@ -133,7 +133,7 @@ sanitize_album(cdtext &text) if (space != NULL) { space[0] = '\0'; text.artist = text.album; - text.album = strdup(space + 2); + text.album = copy_string(space + 2); sanitize_string(text.artist); sanitize_string(text.album); @@ -142,26 +142,28 @@ sanitize_album(cdtext &text) } -void +static void sanitize_titles(cdtext &text) { for (uint8 i = 0; i < text.track_count; i++) { cut_string(text.titles[i], "(Album Version)"); sanitize_string(text.titles[i]); sanitize_string(text.artists[i]); - if (!strcasecmp(text.artists[i], text.artist)) { + + if (text.artists[i] != NULL && text.artist != NULL + && !strcasecmp(text.artists[i], text.artist)) { // if the title artist is the same as the main artist, remove it free(text.artists[i]); text.artists[i] = NULL; } if (text.titles[i] != NULL && text.titles[i][0] == '\t' && i > 0) - text.titles[i] = strdup(text.titles[i - 1]); + text.titles[i] = copy_string(text.titles[i - 1]); } } -bool +static bool single_case(const char *string, bool &upper, bool &first) { if (string == NULL) @@ -185,7 +187,7 @@ single_case(const char *string, bool &upper, bool &first) } -void +static void capitalize_string(char *string) { if (string == NULL) @@ -207,7 +209,7 @@ capitalize_string(char *string) } -void +static void correct_case(cdtext &text) { // check if all titles share a single case @@ -238,14 +240,39 @@ correct_case(cdtext &text) // #pragma mark - CD-Text -bool +cdtext::cdtext() + : + artist(NULL), + album(NULL), + genre(NULL), + track_count(0) +{ + memset(titles, 0, sizeof(titles)); + memset(artists, 0, sizeof(artists)); +} + + +cdtext::~cdtext() +{ + free(album); + free(artist); + free(genre); + + for (uint8 i = 0; i < track_count; i++) { + free(titles[i]); + free(artists[i]); + } +} + + +static bool is_string_id(uint8 id) { return id >= kTrackID && id <= kMessageID; } -bool +static bool parse_pack_data(cdtext_pack_data *&pack, uint32 &packLeft, cdtext_pack_data *&lastPack, uint8 &id, uint8 &track, uint8 &state, char *buffer, size_t &length) @@ -280,13 +307,13 @@ parse_pack_data(cdtext_pack_data *&pack, uint32 &packLeft, while (id == pack->id && track == pack->track) { #if 1 - printf("%u.%u.%u, %u.%u.%u, ", pack->id, pack->track, pack->number, + dprintf("%u.%u.%u, %u.%u.%u, ", pack->id, pack->track, pack->number, pack->double_byte, pack->block_number, pack->character_position); for (int32 i = 0; i < 12; i++) { if (isprint(pack->text[i])) - putchar(pack->text[i]); + dprintf("%c", pack->text[i]); } - putchar('\n'); + dprintf("\n"); #endif if (is_string_id(id)) { // TODO: support double byte characters @@ -330,72 +357,22 @@ parse_pack_data(cdtext_pack_data *&pack, uint32 &packLeft, } -void -dump_cdtext(cdtext_pack_data *pack, size_t packLength) +static void +dump_cdtext(cdtext &text) { - cdtext_pack_data *lastPack = NULL; - char buffer[256]; - uint8 state = 0; - cdtext text; - - memset(&text, 0, sizeof(cdtext)); - - while (true) { - size_t length = sizeof(buffer); - uint8 id, track; - - if (!parse_pack_data(pack, packLength, lastPack, id, track, - state, buffer, length)) - break; - - switch (id) { - case kTrackID: - if (track == 0) { - if (text.album == NULL) - text.album = strdup(buffer); - } else if (track <= kMaxTracks) { - if (text.titles[track - 1] == NULL) - text.titles[track - 1] = strdup(buffer); - if (track > text.track_count) - text.track_count = track; - } - break; - - case kArtistID: - if (track == 0) { - if (text.artist == NULL) - text.artist = strdup(buffer); - } else if (track <= kMaxTracks) { - if (text.artists[track - 1] == NULL) - text.artists[track - 1] = strdup(buffer); - } - break; - - default: - if (is_string_id(id)) - printf("UNKNOWN %u: \"%s\"\n", id, buffer); - break; - } - } - - sanitize_string(text.artist); - sanitize_album(text); - sanitize_titles(text); - correct_case(text); - if (text.album) - printf("Album: \"%s\"\n", text.album); + dprintf("Album: \"%s\"\n", text.album); if (text.artist) - printf("Artist: \"%s\"\n", text.artist); + dprintf("Artist: \"%s\"\n", text.artist); for (uint8 i = 0; i < text.track_count; i++) { - printf("Track %02u: \"%s\"%s%s%s\n", i + 1, text.titles[i], + dprintf("Track %02u: \"%s\"%s%s%s\n", i + 1, text.titles[i], text.artists[i] ? " (" : "", text.artists[i] ? text.artists[i] : "", text.artists[i] ? ")" : ""); } } -void +static void dump_toc(scsi_toc_toc *toc) { int32 numTracks = toc->last_track + 1 - toc->first_track; @@ -415,22 +392,24 @@ dump_toc(scsi_toc_toc *toc) length.second = (diff % kFramesPerMinute) / kFramesPerSecond; length.frame = diff % kFramesPerSecond; - printf("%02u. %02u:%02u.%02u (length %02u:%02u.%02u)\n", + dprintf("%02u. %02u:%02u.%02u (length %02u:%02u.%02u)\n", track.track_number, start.minute, start.second, start.frame, length.minute, length.second, length.frame); } } -status_t +static status_t read_table_of_contents(int fd, uint32 track, uint8 format, uint8 *buffer, size_t bufferSize) { raw_device_command raw; - uint8 senseData[1024]; + uint8 *senseData = (uint8 *)malloc(kSenseSize); + if (senseData == NULL) + return B_NO_MEMORY; memset(&raw, 0, sizeof(raw_device_command)); - memset(senseData, 0, sizeof(senseData)); + memset(senseData, 0, kSenseSize); memset(buffer, 0, bufferSize); scsi_cmd_read_toc &toc = *(scsi_cmd_read_toc*)&raw.command; @@ -449,84 +428,106 @@ read_table_of_contents(int fd, uint32 track, uint8 format, uint8 *buffer, raw.data_length = bufferSize; raw.timeout = 10000000LL; // 10 secs raw.sense_data = senseData; - raw.sense_data_length = sizeof(senseData); + raw.sense_data_length = sizeof(kSenseSize); - if (ioctl(fd, B_RAW_DEVICE_COMMAND, &raw) == 0) { - if (raw.scsi_status == 0 && raw.cam_status == 1) { - puts("success!\n"); - } else { - puts("failure!\n"); - } + if (ioctl(fd, B_RAW_DEVICE_COMMAND, &raw) == 0 + && raw.scsi_status == 0 && raw.cam_status == 1) { + free(senseData); + return B_OK; } - return 0; + free(senseData); + return B_ERROR; } -void -dump_block(const uint8 *buffer, int size, const char *prefix) +// #pragma mark - exported functions + + +status_t +read_cdtext(int fd, struct cdtext &cdtext) { - int i; - - for (i = 0; i < size;) - { - int start = i; + uint8 *buffer = (uint8 *)malloc(kBufferSize); + if (buffer == NULL) + return B_NO_MEMORY; - printf(prefix); - for (; i < start+16; i++) - { - if (!(i % 4)) - printf(" "); - - if (i >= size) - printf(" "); - else - printf("%02x", *(unsigned char *)(buffer + i)); - } - printf(" "); - - for (i = start; i < start + 16; i++) - { - if (i < size) - { - char c = buffer[i]; - - if (c < 30) - printf("."); - else - printf("%c", c); - } - else - break; - } - printf("\n"); + // do it twice, just in case... + // (at least my CD-ROM sometimes returned broken data on first try) + read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_CD_TEXT, buffer, + kBufferSize); + if (read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_CD_TEXT, buffer, + kBufferSize) < B_OK) { + free(buffer); + return B_ERROR; } -} - -int -main(int argc, char** argv) -{ - int fd = open(argv[1], O_RDONLY); - if (fd < 0) - return -1; - - uint8 buffer[1024]; scsi_toc_general *header = (scsi_toc_general *)buffer; - read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_TOC, buffer, sizeof(buffer)); - header->data_length = B_BENDIAN_TO_HOST_INT16(header->data_length); - printf("TOC header %u, %d, %d\n", header->data_length, header->first, header->last); - //dump_block(buffer, header->data_length + 2, "TOC"); - dump_toc((scsi_toc_toc *)buffer); + size_t packLength = B_BENDIAN_TO_HOST_INT16(header->data_length) - 2; + cdtext_pack_data *pack = (cdtext_pack_data *)(header + 1); + cdtext_pack_data *lastPack = NULL; + uint8 state = 0; + char text[256]; - read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_CD_TEXT, buffer, sizeof(buffer)); - read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_CD_TEXT, buffer, sizeof(buffer)); - header->data_length = B_BENDIAN_TO_HOST_INT16(header->data_length); - printf("TEXT header %u, %d, %d\n", header->data_length, header->first, header->last); - //dump_block(buffer, header->data_length + 2, "TEXT"); + while (true) { + size_t length = sizeof(text); + uint8 id, track; - dump_cdtext((cdtext_pack_data *)(buffer + 4), header->data_length - 2); + if (!parse_pack_data(pack, packLength, lastPack, id, track, + state, text, length)) + break; - close(fd); + switch (id) { + case kTrackID: + if (track == 0) { + if (cdtext.album == NULL) + cdtext.album = copy_string(text); + } else if (track <= kMaxTracks) { + if (cdtext.titles[track - 1] == NULL) + cdtext.titles[track - 1] = copy_string(text); + if (track > cdtext.track_count) + cdtext.track_count = track; + } + break; + + case kArtistID: + if (track == 0) { + if (cdtext.artist == NULL) + cdtext.artist = copy_string(text); + } else if (track <= kMaxTracks) { + if (cdtext.artists[track - 1] == NULL) + cdtext.artists[track - 1] = copy_string(text); + } + break; + + default: + if (is_string_id(id)) + dprintf("UNKNOWN %u: \"%s\"\n", id, text); + break; + } + } + + free(buffer); + + sanitize_string(cdtext.artist); + sanitize_album(cdtext); + sanitize_titles(cdtext); + correct_case(cdtext); + + dump_cdtext(cdtext); + return B_OK; } + + +status_t +read_table_of_contents(int fd, scsi_toc_toc *toc, size_t length) +{ + status_t status = read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_TOC, + (uint8*)toc, length); + if (status < B_OK) + return status; + + dump_toc(toc); + return B_OK; +} + diff --git a/src/add-ons/kernel/file_systems/cdda/cdda.h b/src/add-ons/kernel/file_systems/cdda/cdda.h new file mode 100644 index 0000000000..3b2e60f0b8 --- /dev/null +++ b/src/add-ons/kernel/file_systems/cdda/cdda.h @@ -0,0 +1,34 @@ +/* + * Copyright 2007, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ +#ifndef CDDA_H +#define CDDA_H + + +#include + + +static const uint32 kFramesPerSecond = 75; +static const uint32 kFramesPerMinute = kFramesPerSecond * 60; +static const uint32 kFrameSize = 2352; +static const uint8 kMaxTracks = 0x63; + +struct cdtext { + cdtext(); + ~cdtext(); + + char *artist; + char *album; + char *genre; + + char *titles[kMaxTracks]; + char *artists[kMaxTracks]; + uint8 track_count; +}; + + +status_t read_cdtext(int fd, cdtext &text); +status_t read_table_of_contents(int fd, scsi_toc_toc *toc, size_t length); + +#endif // CDDA_H diff --git a/src/add-ons/kernel/file_systems/cdda/kernel_interface.cpp b/src/add-ons/kernel/file_systems/cdda/kernel_interface.cpp index 198b69152f..9d70fc5531 100644 --- a/src/add-ons/kernel/file_systems/cdda/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/cdda/kernel_interface.cpp @@ -4,6 +4,7 @@ */ +#include "cdda.h" #include "Lock.h" #include @@ -57,7 +58,8 @@ class Volume { off_t NumBlocks() const { return fNumBlocks; } private: - Inode *_CreateNode(Inode *parent, const char *name, int32 type); + Inode *_CreateNode(Inode *parent, const char *name, + off_t start, off_t frames, int32 type); Semaphore fLock; int fDevice; @@ -74,7 +76,8 @@ class Volume { class Inode { public: - Inode(Volume *volume, Inode *parent, const char *name, int32 type); + Inode(Volume *volume, Inode *parent, const char *name, off_t start, + off_t frames, int32 type); ~Inode(); status_t InitCheck(); @@ -93,8 +96,12 @@ class Inode { { return fCreationTime; } time_t ModificationTime() const { return fModificationTime; } + off_t StartFrame() const + { return fStartFrame; } + off_t FrameCount() const + { return fFrameCount; } off_t Size() const - { return fSize; } + { return fFrameCount * kFrameSize /* + WAV header */; } Inode *Next() const { return fNext; } void SetNext(Inode *inode) { fNext = inode; } @@ -103,12 +110,13 @@ class Inode { Inode *fNext; vnode_id fID; int32 fType; - const char *fName; + char *fName; gid_t fGroupID; uid_t fUserID; time_t fCreationTime; time_t fModificationTime; - off_t fSize; + off_t fStartFrame; + off_t fFrameCount; }; @@ -142,7 +150,7 @@ Volume::Volume(mount_id id) fFirstEntry(NULL) { // create the root vnode - fRootNode = _CreateNode(NULL, "", S_IFDIR | 0777); + fRootNode = _CreateNode(NULL, "", 0, 0, S_IFDIR | 0777); } @@ -160,6 +168,8 @@ Volume::~Volume() next = inode->Next(); delete inode; } + + free(fName); } @@ -181,6 +191,64 @@ Volume::Mount(const char* device) if (fDevice < 0) return errno; + scsi_toc_toc *toc = (scsi_toc_toc *)malloc(1024); + if (toc == NULL) + return B_NO_MEMORY; + + status_t status = read_table_of_contents(fDevice, toc, 1024); + if (status < B_OK) { + free(toc); + return status; + } + + cdtext text; + if (read_cdtext(fDevice, text) < B_OK) + dprintf("CDDA: no CD-Text found.\n"); + + int32 trackCount = toc->last_track + 1 - toc->first_track; + char title[256]; + + for (int32 i = 0; i < trackCount; i++) { + scsi_cd_msf& next = toc->tracks[i + 1].start.time; + // the last track is always lead-out + scsi_cd_msf& start = toc->tracks[i].start.time; + + off_t startFrame = start.minute * kFramesPerMinute + + start.second * kFramesPerSecond + start.frame; + off_t frames = next.minute * kFramesPerMinute + + next.second * kFramesPerSecond + next.frame + - startFrame; + + if (text.titles[i] != NULL && text.titles[i]) { + if (text.artists[i] != NULL && text.artists[i]) { + snprintf(title, sizeof(title), "%02ld. %s - %s.wav", i + 1, + text.artists[i], text.titles[i]); + } else { + snprintf(title, sizeof(title), "%02ld. %s.wav", i + 1, + text.titles[i]); + } + } else + snprintf(title, sizeof(title), "%02ld.wav", i + 1); + + _CreateNode(fRootNode, title, startFrame, frames, S_IFREG | 0444); + } + + free(toc); + + // determine volume title + + if (text.artist != NULL && text.album != NULL) + snprintf(title, sizeof(title), "%s - %s", text.artist, text.album); + else if (text.artist != NULL || text.album != NULL) { + snprintf(title, sizeof(title), "%s", text.artist != NULL + ? text.artist : text.album); + } else + strcpy(title, "Audio CD"); + + fName = strdup(title); + if (fName == NULL) + return B_NO_MEMORY; + return B_OK; } @@ -193,9 +261,10 @@ Volume::Lock() Inode * -Volume::_CreateNode(Inode *parent, const char *name, int32 type) +Volume::_CreateNode(Inode *parent, const char *name, off_t start, off_t frames, + int32 type) { - Inode *inode = new Inode(this, parent, name, type); + Inode *inode = new Inode(this, parent, name, start, frames, type); if (inode == NULL) return NULL; @@ -245,14 +314,24 @@ Volume::Find(const char *name) status_t Volume::SetName(const char *name) { - return B_ERROR; + if (name == NULL || !name[0]) + return B_BAD_VALUE; + + name = strdup(name); + if (name == NULL) + return B_NO_MEMORY; + + free(fName); + fName = (char *)name; + return B_OK; } // #pragma mark - -Inode::Inode(Volume *volume, Inode *parent, const char *name, int32 type) +Inode::Inode(Volume *volume, Inode *parent, const char *name, off_t start, + off_t frames, int32 type) : fNext(NULL) { @@ -262,6 +341,8 @@ Inode::Inode(Volume *volume, Inode *parent, const char *name, int32 type) fID = volume->GetNextNodeID(); fType = type; + fStartFrame = start; + fFrameCount = frames; fUserID = geteuid(); fGroupID = parent ? parent->GroupID() : getegid(); @@ -289,7 +370,16 @@ Inode::InitCheck() status_t Inode::SetName(const char* name) { - return B_ERROR; + if (name == NULL || !name[0]) + return B_BAD_VALUE; + + name = strdup(name); + if (name == NULL) + return B_NO_MEMORY; + + free(fName); + fName = (char *)name; + return B_OK; } @@ -578,7 +668,19 @@ cdda_rename(fs_volume _volume, void *_oldDir, const char *oldName, void *_newDir || strchr(newName, '/') != NULL) return B_BAD_VALUE; - return B_ERROR; + // we only have a single directory which simplifies things a bit :-) + + Volume *volume = (Volume *)_volume; + Locker _(volume->Lock()); + + Inode *inode = volume->Find(oldName); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + if (volume->Find(newName) != NULL) + return B_NAME_IN_USE; + + return inode->SetName(newName); } diff --git a/src/tests/add-ons/kernel/file_systems/Jamfile b/src/tests/add-ons/kernel/file_systems/Jamfile index 1c8c7abc3b..c7786f4b53 100644 --- a/src/tests/add-ons/kernel/file_systems/Jamfile +++ b/src/tests/add-ons/kernel/file_systems/Jamfile @@ -1,6 +1,7 @@ SubDir HAIKU_TOP src tests add-ons kernel file_systems ; SubInclude HAIKU_TOP src tests add-ons kernel file_systems bfs ; +SubInclude HAIKU_TOP src tests add-ons kernel file_systems cdda ; SubInclude HAIKU_TOP src tests add-ons kernel file_systems fs_shell ; #SubInclude HAIKU_TOP src tests add-ons kernel file_systems iso9660 ; SubInclude HAIKU_TOP src tests add-ons kernel file_systems udf ; diff --git a/src/tests/add-ons/kernel/file_systems/cdda/Jamfile b/src/tests/add-ons/kernel/file_systems/cdda/Jamfile new file mode 100644 index 0000000000..1c9b0fb865 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/cdda/Jamfile @@ -0,0 +1,9 @@ +SubDir HAIKU_TOP src tests add-ons kernel file_systems cdda ; + +SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src add-ons kernel file_systems cdda ] ; + +SimpleTest cdda_text + : + cdda_text.cpp + cdda.cpp +; diff --git a/src/tests/add-ons/kernel/file_systems/cdda/cdda_text.cpp b/src/tests/add-ons/kernel/file_systems/cdda/cdda_text.cpp new file mode 100644 index 0000000000..cd0aa7a8bc --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/cdda/cdda_text.cpp @@ -0,0 +1,48 @@ +/* + * Copyright 2007, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ + + +#include "cdda.h" + +#include +#include +#include +#include + + +extern const char* __progname; + + +extern "C" void +dprintf(const char* format, ...) +{ + va_list args; + va_start(args, format); + vprintf(format, args); + fflush(stdout); + va_end(args); +} + + +int +main(int argc, char** argv) +{ + if (argc < 2) + return -1; + int fd = open(argv[1], O_RDONLY); + if (fd < 0) + return -1; + + uint8 buffer[1024]; + if (read_table_of_contents(fd, (scsi_toc_toc*)buffer, sizeof(buffer)) < 0) { + fprintf(stderr, "%s: Retrieving TOC failed", __progname); + return -1; + } + + cdtext text; + read_cdtext(fd, text); + + close(fd); +}