/* libFLAC - Free Lossless Audio Codec library
- * Copyright (C) 2001,2002,2003,2004,2005,2006 Josh Coalson
+ * Copyright (C) 2001,2002,2003,2004,2005,2006,2007,2008,2009 Josh Coalson
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
#include <stdlib.h>
#include <string.h>
-#if defined _MSC_VER || defined __MINGW32__
+#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__
+#if defined __BORLANDC__
+#include <utime.h> /* for utime() */
+#else
#include <sys/utime.h> /* for utime() */
+#endif
#include <io.h> /* for chmod() */
#include <sys/types.h> /* for off_t */
-//@@@ [2G limit] hacks for MSVC6
+#if _MSC_VER <= 1600 || defined __BORLANDC__ /* @@@ [2G limit] */
#define fseeko fseek
#define ftello ftell
+#endif
#else
#include <sys/types.h> /* some flavors of BSD (like OS X) require this to get time_t */
#include <utime.h> /* for utime() */
#include "FLAC/assert.h"
#include "FLAC/stream_decoder.h"
+#include "share/alloc.h"
#ifdef max
#undef max
FLAC__stream_decoder_set_metadata_respond(decoder, type);
if(FLAC__stream_decoder_init_file(decoder, filename, write_callback_, metadata_callback_, error_callback_, &cd) != FLAC__STREAM_DECODER_INIT_STATUS_OK || cd.got_error) {
- FLAC__stream_decoder_finish(decoder);
+ (void)FLAC__stream_decoder_finish(decoder);
FLAC__stream_decoder_delete(decoder);
return 0;
}
if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder) || cd.got_error) {
- FLAC__stream_decoder_finish(decoder);
+ (void)FLAC__stream_decoder_finish(decoder);
FLAC__stream_decoder_delete(decoder);
if(0 != cd.object)
FLAC__metadata_object_delete(cd.object);
return 0;
}
- FLAC__stream_decoder_finish(decoder);
+ (void)FLAC__stream_decoder_finish(decoder);
FLAC__stream_decoder_delete(decoder);
return cd.object;
};
-FLAC_API FLAC__Metadata_SimpleIterator *FLAC__metadata_simple_iterator_new()
+FLAC_API FLAC__Metadata_SimpleIterator *FLAC__metadata_simple_iterator_new(void)
{
FLAC__Metadata_SimpleIterator *iterator = (FLAC__Metadata_SimpleIterator*)calloc(1, sizeof(FLAC__Metadata_SimpleIterator));
return true;
}
+/*@@@@add to tests*/
+FLAC_API FLAC__bool FLAC__metadata_simple_iterator_is_last(const FLAC__Metadata_SimpleIterator *iterator)
+{
+ FLAC__ASSERT(0 != iterator);
+ FLAC__ASSERT(0 != iterator->file);
+
+ return iterator->is_last;
+}
+
+/*@@@@add to tests*/
+FLAC_API off_t FLAC__metadata_simple_iterator_get_block_offset(const FLAC__Metadata_SimpleIterator *iterator)
+{
+ FLAC__ASSERT(0 != iterator);
+ FLAC__ASSERT(0 != iterator->file);
+
+ return iterator->offset[iterator->depth];
+}
+
FLAC_API FLAC__MetadataType FLAC__metadata_simple_iterator_get_block_type(const FLAC__Metadata_SimpleIterator *iterator)
{
FLAC__ASSERT(0 != iterator);
return iterator->type;
}
+/*@@@@add to tests*/
+FLAC_API unsigned FLAC__metadata_simple_iterator_get_block_length(const FLAC__Metadata_SimpleIterator *iterator)
+{
+ FLAC__ASSERT(0 != iterator);
+ FLAC__ASSERT(0 != iterator->file);
+
+ return iterator->length;
+}
+
+/*@@@@add to tests*/
+FLAC_API FLAC__bool FLAC__metadata_simple_iterator_get_application_id(FLAC__Metadata_SimpleIterator *iterator, FLAC__byte *id)
+{
+ const unsigned id_bytes = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8;
+
+ FLAC__ASSERT(0 != iterator);
+ FLAC__ASSERT(0 != iterator->file);
+ FLAC__ASSERT(0 != id);
+
+ if(iterator->type != FLAC__METADATA_TYPE_APPLICATION) {
+ iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT;
+ return false;
+ }
+
+ if(fread(id, 1, id_bytes, iterator->file) != id_bytes) {
+ iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+ return false;
+ }
+
+ /* back up */
+ if(0 != fseeko(iterator->file, -((int)id_bytes), SEEK_CUR)) {
+ iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
+ return false;
+ }
+
+ return true;
+}
+
FLAC_API FLAC__StreamMetadata *FLAC__metadata_simple_iterator_get_block(FLAC__Metadata_SimpleIterator *iterator)
{
FLAC__StreamMetadata *block = FLAC__metadata_object_new(iterator->type);
struct FLAC__Metadata_Chain {
char *filename; /* will be NULL if using callbacks */
+ FLAC__bool is_ogg;
FLAC__Metadata_Node *head;
FLAC__Metadata_Node *tail;
unsigned nodes;
* or not the whole file has to be rewritten.
*/
off_t initial_length;
+ /* @@@ hacky, these are currently only needed by ogg reader */
+ FLAC__IOHandle handle;
+ FLAC__IOCallback_Read read_cb;
};
struct FLAC__Metadata_Iterator {
};
-static FLAC__Metadata_Node *node_new_()
+static FLAC__Metadata_Node *node_new_(void)
{
return (FLAC__Metadata_Node*)calloc(1, sizeof(FLAC__Metadata_Node));
}
FLAC__ASSERT(0 != chain);
chain->filename = 0;
+ chain->is_ogg = false;
chain->head = chain->tail = 0;
chain->nodes = 0;
chain->status = FLAC__METADATA_CHAIN_STATUS_OK;
chain->initial_length = 0;
+ chain->read_cb = 0;
}
static void chain_clear_(FLAC__Metadata_Chain *chain)
return true;
}
+static FLAC__StreamDecoderReadStatus chain_read_ogg_read_cb_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
+{
+ FLAC__Metadata_Chain *chain = (FLAC__Metadata_Chain*)client_data;
+ (void)decoder;
+ if(*bytes > 0 && chain->status == FLAC__METADATA_CHAIN_STATUS_OK) {
+ *bytes = chain->read_cb(buffer, sizeof(FLAC__byte), *bytes, chain->handle);
+ if(*bytes == 0)
+ return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+ else
+ return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+ }
+ else
+ return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+}
+
+static FLAC__StreamDecoderWriteStatus chain_read_ogg_write_cb_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
+{
+ (void)decoder, (void)frame, (void)buffer, (void)client_data;
+ return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+}
+
+static void chain_read_ogg_metadata_cb_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
+{
+ FLAC__Metadata_Chain *chain = (FLAC__Metadata_Chain*)client_data;
+ FLAC__Metadata_Node *node;
+
+ (void)decoder;
+
+ node = node_new_();
+ if(0 == node) {
+ chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
+
+ node->data = FLAC__metadata_object_clone(metadata);
+ if(0 == node->data) {
+ node_delete_(node);
+ chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
+
+ chain_append_node_(chain, node);
+}
+
+static void chain_read_ogg_error_cb_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
+{
+ FLAC__Metadata_Chain *chain = (FLAC__Metadata_Chain*)client_data;
+ (void)decoder, (void)status;
+ chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; /*@@@ maybe needs better error code */
+}
+
+static FLAC__bool chain_read_ogg_cb_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb)
+{
+ FLAC__StreamDecoder *decoder;
+
+ FLAC__ASSERT(0 != chain);
+
+ /* we assume we're already at the beginning of the file */
+
+ chain->handle = handle;
+ chain->read_cb = read_cb;
+ if(0 == (decoder = FLAC__stream_decoder_new())) {
+ chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
+ return false;
+ }
+ FLAC__stream_decoder_set_metadata_respond_all(decoder);
+ if(FLAC__stream_decoder_init_ogg_stream(decoder, chain_read_ogg_read_cb_, /*seek_callback=*/0, /*tell_callback=*/0, /*length_callback=*/0, /*eof_callback=*/0, chain_read_ogg_write_cb_, chain_read_ogg_metadata_cb_, chain_read_ogg_error_cb_, chain) != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
+ FLAC__stream_decoder_delete(decoder);
+ chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; /*@@@ maybe needs better error code */
+ return false;
+ }
+
+ chain->first_offset = 0; /*@@@ wrong; will need to be set correctly to implement metadata writing for Ogg FLAC */
+
+ if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder))
+ chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; /*@@@ maybe needs better error code */
+ if(chain->status != FLAC__METADATA_CHAIN_STATUS_OK) {
+ FLAC__stream_decoder_delete(decoder);
+ return false;
+ }
+
+ FLAC__stream_decoder_delete(decoder);
+
+ chain->last_offset = 0; /*@@@ wrong; will need to be set correctly to implement metadata writing for Ogg FLAC */
+
+ chain->initial_length = chain_calculate_length_(chain);
+
+ return true;
+}
+
static FLAC__bool chain_rewrite_metadata_in_place_cb_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, FLAC__IOCallback_Seek seek_cb)
{
FLAC__Metadata_Node *node;
return true;
}
-FLAC_API FLAC__Metadata_Chain *FLAC__metadata_chain_new()
+FLAC_API FLAC__Metadata_Chain *FLAC__metadata_chain_new(void)
{
FLAC__Metadata_Chain *chain = (FLAC__Metadata_Chain*)calloc(1, sizeof(FLAC__Metadata_Chain));
return status;
}
-FLAC_API FLAC__bool FLAC__metadata_chain_read(FLAC__Metadata_Chain *chain, const char *filename)
+static FLAC__bool chain_read_(FLAC__Metadata_Chain *chain, const char *filename, FLAC__bool is_ogg)
{
FILE *file;
FLAC__bool ret;
return false;
}
+ chain->is_ogg = is_ogg;
+
if(0 == (file = fopen(filename, "rb"))) {
chain->status = FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE;
return false;
}
- /* chain_read_cb_() sets chain->status for us */
- ret = chain_read_cb_(chain, file, (FLAC__IOCallback_Read)fread, fseek_wrapper_, ftell_wrapper_);
+ /* the function also sets chain->status for us */
+ ret = is_ogg?
+ chain_read_ogg_cb_(chain, file, (FLAC__IOCallback_Read)fread) :
+ chain_read_cb_(chain, file, (FLAC__IOCallback_Read)fread, fseek_wrapper_, ftell_wrapper_)
+ ;
fclose(file);
return ret;
}
-FLAC_API FLAC__bool FLAC__metadata_chain_read_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks)
+FLAC_API FLAC__bool FLAC__metadata_chain_read(FLAC__Metadata_Chain *chain, const char *filename)
+{
+ return chain_read_(chain, filename, /*is_ogg=*/false);
+}
+
+/*@@@@add to tests*/
+FLAC_API FLAC__bool FLAC__metadata_chain_read_ogg(FLAC__Metadata_Chain *chain, const char *filename)
+{
+ return chain_read_(chain, filename, /*is_ogg=*/true);
+}
+
+static FLAC__bool chain_read_with_callbacks_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks, FLAC__bool is_ogg)
{
+ FLAC__bool ret;
+
FLAC__ASSERT(0 != chain);
chain_clear_(chain);
return false;
}
+ chain->is_ogg = is_ogg;
+
/* rewind */
if(0 != callbacks.seek(handle, 0, SEEK_SET)) {
chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR;
return false;
}
- if(!chain_read_cb_(chain, handle, callbacks.read, callbacks.seek, callbacks.tell))
- return false; /* chain->status is already set by chain_read_cb_ */
+ /* the function also sets chain->status for us */
+ ret = is_ogg?
+ chain_read_ogg_cb_(chain, handle, callbacks.read) :
+ chain_read_cb_(chain, handle, callbacks.read, callbacks.seek, callbacks.tell)
+ ;
- return true;
+ return ret;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_chain_read_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks)
+{
+ return chain_read_with_callbacks_(chain, handle, callbacks, /*is_ogg=*/false);
+}
+
+/*@@@@add to tests*/
+FLAC_API FLAC__bool FLAC__metadata_chain_read_ogg_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks)
+{
+ return chain_read_with_callbacks_(chain, handle, callbacks, /*is_ogg=*/true);
}
FLAC_API FLAC__bool FLAC__metadata_chain_check_if_tempfile_needed(FLAC__Metadata_Chain *chain, FLAC__bool use_padding)
FLAC__ASSERT(0 != chain);
+ if (chain->is_ogg) { /* cannot write back to Ogg FLAC yet */
+ chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR;
+ return false;
+ }
+
if (0 == chain->filename) {
chain->status = FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH;
return false;
FLAC__ASSERT(0 != chain);
+ if (chain->is_ogg) { /* cannot write back to Ogg FLAC yet */
+ chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR;
+ return false;
+ }
+
if (0 != chain->filename) {
chain->status = FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH;
return false;
FLAC__ASSERT(0 != chain);
+ if (chain->is_ogg) { /* cannot write back to Ogg FLAC yet */
+ chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR;
+ return false;
+ }
+
if (0 != chain->filename) {
chain->status = FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH;
return false;
}
-FLAC_API FLAC__Metadata_Iterator *FLAC__metadata_iterator_new()
+FLAC_API FLAC__Metadata_Iterator *FLAC__metadata_iterator_new(void)
{
FLAC__Metadata_Iterator *iterator = (FLAC__Metadata_Iterator*)calloc(1, sizeof(FLAC__Metadata_Iterator));
if(read_cb(block->id, 1, id_bytes, handle) != id_bytes)
return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+ if(block_length < id_bytes)
+ return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+
block_length -= id_bytes;
if(block_length == 0) {
if(block->num_points == 0)
block->points = 0;
- else if(0 == (block->points = (FLAC__StreamMetadata_SeekPoint*)malloc(block->num_points * sizeof(FLAC__StreamMetadata_SeekPoint))))
+ else if(0 == (block->points = (FLAC__StreamMetadata_SeekPoint*)safe_malloc_mul_2op_(block->num_points, /*times*/sizeof(FLAC__StreamMetadata_SeekPoint))))
return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
for(i = 0; i < block->num_points; i++) {
entry->entry = 0;
}
else {
- if(0 == (entry->entry = (FLAC__byte*)malloc(entry->length+1)))
+ if(0 == (entry->entry = (FLAC__byte*)safe_malloc_add_2op_(entry->length, /*+*/1)))
return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
if(read_cb(entry->entry, 1, entry->length, handle) != entry->length)
return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
}
-FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_picture_cstring_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__byte **data, FLAC__uint32 *length, FLAC__uint32 length_len)
+static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_picture_cstring_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__byte **data, FLAC__uint32 *length, FLAC__uint32 length_len)
{
FLAC__byte buffer[sizeof(FLAC__uint32)];
if(0 != *data)
free(*data);
- if(0 == (*data = (FLAC__byte*)malloc(*length+1)))
+ if(0 == (*data = (FLAC__byte*)safe_malloc_add_2op_(*length, /*+*/1)))
return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
if(*length > 0) {
FLAC__byte buffer[8192];
size_t n;
+ FLAC__ASSERT(bytes >= 0);
while(bytes > 0) {
- n = min(sizeof(buffer), bytes);
+ n = min(sizeof(buffer), (size_t)bytes);
if(fread(buffer, 1, n, file) != n) {
*status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
return false;
FLAC__byte buffer[8192];
size_t n;
+ FLAC__ASSERT(bytes >= 0);
while(bytes > 0) {
- n = min(sizeof(buffer), bytes);
+ n = min(sizeof(buffer), (size_t)bytes);
if(read_cb(buffer, 1, n, handle) != n) {
*status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
return false;
{
static const char *tempfile_suffix = ".metadata_edit";
if(0 == tempfile_path_prefix) {
- if(0 == (*tempfilename = (char*)malloc(strlen(filename) + strlen(tempfile_suffix) + 1))) {
+ if(0 == (*tempfilename = (char*)safe_malloc_add_3op_(strlen(filename), /*+*/strlen(tempfile_suffix), /*+*/1))) {
*status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
return false;
}
else
p++;
- if(0 == (*tempfilename = (char*)malloc(strlen(tempfile_path_prefix) + 1 + strlen(p) + strlen(tempfile_suffix) + 1))) {
+ if(0 == (*tempfilename = (char*)safe_malloc_add_4op_(strlen(tempfile_path_prefix), /*+*/strlen(p), /*+*/strlen(tempfile_suffix), /*+*/2))) {
*status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
return false;
}
(void)fclose(*tempfile);
*tempfile = 0;
-#if defined _MSC_VER || defined __MINGW32__ || defined __EMX__
+#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ || defined __EMX__
/* on some flavors of windows, rename() will fail if the destination already exists */
if(unlink(filename) < 0) {
cleanup_tempfile_(tempfile, tempfilename);
srctime.modtime = stats->st_mtime;
(void)chmod(filename, stats->st_mode);
(void)utime(filename, &srctime);
-#if !defined _MSC_VER && !defined __MINGW32__ && !defined __EMX__
+#if !defined _MSC_VER && !defined __BORLANDC__ && !defined __MINGW32__ && !defined __EMX__
(void)chown(filename, stats->st_uid, -1);
(void)chown(filename, -1, stats->st_gid);
#endif