/* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2001-2009 Josh Coalson * Copyright (C) 2011-2016 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of the Xiph.org Foundation nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <errno.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <stdarg.h> #include <sys/stat.h> /* for stat(), maybe chmod() */ #include "private/metadata.h" #include "FLAC/stream_decoder.h"#include "share/alloc.h"#include "share/compat.h"#include "share/macros.h"#include "share/safe_str.h"#include "private/macros.h"#include "private/memory.h" // 8bb: hide POSIX warnings#ifdef _MSC_VER#pragma warning(disable: 4996)#endif /* Alias the first (in share/alloc.h) to the second (in src/libFLAC/memory.c). */#define safe_malloc_mul_2op_ safe_malloc_mul_2op_p /**************************************************************************** * * Local function declarations * ***************************************************************************/ static void pack_uint32_(FLAC__uint32 val, FLAC__byte *b, uint32_t bytes);static void pack_uint32_little_endian_(FLAC__uint32 val, FLAC__byte *b, uint32_t bytes);static void pack_uint64_(FLAC__uint64 val, FLAC__byte *b, uint32_t bytes);static FLAC__uint32 unpack_uint32_(FLAC__byte *b, uint32_t bytes);static FLAC__uint32 unpack_uint32_little_endian_(FLAC__byte *b, uint32_t bytes);static FLAC__uint64 unpack_uint64_(FLAC__byte *b, uint32_t bytes); static FLAC__bool read_metadata_block_header_(FLAC__Metadata_SimpleIterator *iterator);static FLAC__bool read_metadata_block_data_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block);static FLAC__bool read_metadata_block_header_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__bool *is_last, FLAC__MetadataType *type, uint32_t *length);static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata *block);static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_StreamInfo *block);static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_padding_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata_Padding *block, uint32_t block_length);static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Application *block, uint32_t block_length);static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_SeekTable *block, uint32_t block_length);static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_entry_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_VorbisComment_Entry *entry, uint32_t max_length);static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata_VorbisComment *block, uint32_t block_length);static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cuesheet_track_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_CueSheet_Track *track);static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cuesheet_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_CueSheet *block);static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_picture_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Picture *block);static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Unknown *block, uint32_t block_length); static FLAC__bool write_metadata_block_header_(FILE *file, FLAC__Metadata_SimpleIteratorStatus *status, const FLAC__StreamMetadata *block);static FLAC__bool write_metadata_block_data_(FILE *file, FLAC__Metadata_SimpleIteratorStatus *status, const FLAC__StreamMetadata *block);static FLAC__bool write_metadata_block_header_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata *block);static FLAC__bool write_metadata_block_data_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata *block);static FLAC__bool write_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_StreamInfo *block);static FLAC__bool write_metadata_block_data_padding_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Padding *block, uint32_t block_length);static FLAC__bool write_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Application *block, uint32_t block_length);static FLAC__bool write_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_SeekTable *block);static FLAC__bool write_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_VorbisComment *block);static FLAC__bool write_metadata_block_data_cuesheet_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_CueSheet *block);static FLAC__bool write_metadata_block_data_picture_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Picture *block);static FLAC__bool write_metadata_block_data_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Unknown *block, uint32_t block_length); static FLAC__bool write_metadata_block_stationary_(FLAC__Metadata_SimpleIterator *iterator, const FLAC__StreamMetadata *block);static FLAC__bool write_metadata_block_stationary_with_padding_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, uint32_t padding_length, FLAC__bool padding_is_last);static FLAC__bool rewrite_whole_file_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool append); static void simple_iterator_push_(FLAC__Metadata_SimpleIterator *iterator);static FLAC__bool simple_iterator_pop_(FLAC__Metadata_SimpleIterator *iterator); static uint32_t seek_to_first_metadata_block_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb);static uint32_t seek_to_first_metadata_block_(FILE *f); static FLAC__bool simple_iterator_copy_file_prefix_(FLAC__Metadata_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, FLAC__bool append);static FLAC__bool simple_iterator_copy_file_postfix_(FLAC__Metadata_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, int fixup_is_last_code, FLAC__off_t fixup_is_last_flag_offset, FLAC__bool backup); static FLAC__bool copy_n_bytes_from_file_(FILE *file, FILE *tempfile, FLAC__off_t bytes, FLAC__Metadata_SimpleIteratorStatus *status);static FLAC__bool copy_n_bytes_from_file_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb, FLAC__off_t bytes, FLAC__Metadata_SimpleIteratorStatus *status);static FLAC__bool copy_remaining_bytes_from_file_(FILE *file, FILE *tempfile, FLAC__Metadata_SimpleIteratorStatus *status);static FLAC__bool copy_remaining_bytes_from_file_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Eof eof_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb, FLAC__Metadata_SimpleIteratorStatus *status); static FLAC__bool open_tempfile_(const char *filename, const char *tempfile_path_prefix, FILE **tempfile, char **tempfilename, FLAC__Metadata_SimpleIteratorStatus *status);static FLAC__bool transport_tempfile_(const char *filename, FILE **tempfile, char **tempfilename, FLAC__Metadata_SimpleIteratorStatus *status);static void cleanup_tempfile_(FILE **tempfile, char **tempfilename); static FLAC__bool get_file_stats_(const char *filename, struct flac_stat_s *stats);static void set_file_stats_(const char *filename, struct flac_stat_s *stats); static int fseek_wrapper_(FLAC__IOHandle handle, FLAC__int64 offset, int whence);static FLAC__int64 ftell_wrapper_(FLAC__IOHandle handle); static FLAC__Metadata_ChainStatus get_equivalent_status_(FLAC__Metadata_SimpleIteratorStatus status); #ifdef FLAC__VALGRIND_TESTINGstatic size_t local__fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream){ size_t ret = fwrite(ptr, size, nmemb, stream); if(!ferror(stream)) fflush(stream); return ret;}#else#define local__fwrite fwrite#endif /**************************************************************************** * * Level 0 implementation * ***************************************************************************/ static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);static void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);static void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); typedef struct { FLAC__bool got_error; FLAC__StreamMetadata *object;} level0_client_data; static FLAC__StreamMetadata *get_one_metadata_block_(const char *filename, FLAC__MetadataType type){ level0_client_data cd; FLAC__StreamDecoder *decoder; cd.got_error = false; cd.object = 0; decoder = FLAC__stream_decoder_new(); if(0 == decoder) return 0; FLAC__stream_decoder_set_md5_checking(decoder, false); FLAC__stream_decoder_set_metadata_ignore_all(decoder); 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) { (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) { (void)FLAC__stream_decoder_finish(decoder); FLAC__stream_decoder_delete(decoder); if(0 != cd.object) FLAC__metadata_object_delete(cd.object); return 0; } (void)FLAC__stream_decoder_finish(decoder); FLAC__stream_decoder_delete(decoder); return cd.object;} FLAC_API FLAC__bool FLAC__metadata_get_streaminfo(const char *filename, FLAC__StreamMetadata *streaminfo){ FLAC__StreamMetadata *object; object = get_one_metadata_block_(filename, FLAC__METADATA_TYPE_STREAMINFO); if (object) { /* can just copy the contents since STREAMINFO has no internal structure */ *streaminfo = *object; FLAC__metadata_object_delete(object); return true; } else { return false; }} FLAC_API FLAC__bool FLAC__metadata_get_tags(const char *filename, FLAC__StreamMetadata **tags){ *tags = get_one_metadata_block_(filename, FLAC__METADATA_TYPE_VORBIS_COMMENT); return 0 != *tags;} FLAC_API FLAC__bool FLAC__metadata_get_cuesheet(const char *filename, FLAC__StreamMetadata **cuesheet){ *cuesheet = get_one_metadata_block_(filename, FLAC__METADATA_TYPE_CUESHEET); return 0 != *cuesheet;} FLAC__StreamDecoderWriteStatus write_callback_(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_CONTINUE;} void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data){ level0_client_data *cd = (level0_client_data *)client_data; (void)decoder; /* * we assume we only get here when the one metadata block we were * looking for was passed to us */ if(!cd->got_error && 0 == cd->object) { if(0 == (cd->object = FLAC__metadata_object_clone(metadata))) cd->got_error = true; }} void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data){ level0_client_data *cd = (level0_client_data *)client_data; (void)decoder; if(status != FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC) cd->got_error = true;} FLAC_API FLAC__bool FLAC__metadata_get_picture(const char *filename, FLAC__StreamMetadata **picture, FLAC__StreamMetadata_Picture_Type type, const char *mime_type, const FLAC__byte *description, uint32_t max_width, uint32_t max_height, uint32_t max_depth, uint32_t max_colors){ FLAC__Metadata_SimpleIterator *it; FLAC__uint64 max_area_seen = 0; FLAC__uint64 max_depth_seen = 0; *picture = 0; it = FLAC__metadata_simple_iterator_new(); if(0 == it) return false; if(!FLAC__metadata_simple_iterator_init(it, filename, /*read_only=*/true, /*preserve_file_stats=*/true)) { FLAC__metadata_simple_iterator_delete(it); return false; } do { if(FLAC__metadata_simple_iterator_get_block_type(it) == FLAC__METADATA_TYPE_PICTURE) { FLAC__StreamMetadata *obj = FLAC__metadata_simple_iterator_get_block(it); FLAC__uint64 area = (FLAC__uint64)obj->data.picture.width * (FLAC__uint64)obj->data.picture.height; /* check constraints */ if( (type == (FLAC__StreamMetadata_Picture_Type)(-1) || type == obj->data.picture.type) && (mime_type == 0 || !strcmp(mime_type, obj->data.picture.mime_type)) && (description == 0 || !strcmp((const char *)description, (const char *)obj->data.picture.description)) && obj->data.picture.width <= max_width && obj->data.picture.height <= max_height && obj->data.picture.depth <= max_depth && obj->data.picture.colors <= max_colors && (area > max_area_seen || (area == max_area_seen && obj->data.picture.depth > max_depth_seen)) ) { if(*picture) FLAC__metadata_object_delete(*picture); *picture = obj; max_area_seen = area; max_depth_seen = obj->data.picture.depth; } else { FLAC__metadata_object_delete(obj); } } } while(FLAC__metadata_simple_iterator_next(it)); FLAC__metadata_simple_iterator_delete(it); return (0 != *picture);} /**************************************************************************** * * Level 1 implementation * ***************************************************************************/ #define SIMPLE_ITERATOR_MAX_PUSH_DEPTH (1+4)/* 1 for initial offset, +4 for our own personal use */ struct FLAC__Metadata_SimpleIterator { FILE *file; char *filename, *tempfile_path_prefix; struct flac_stat_s stats; FLAC__bool has_stats; FLAC__bool is_writable; FLAC__Metadata_SimpleIteratorStatus status; FLAC__off_t offset[SIMPLE_ITERATOR_MAX_PUSH_DEPTH]; FLAC__off_t first_offset; /* this is the offset to the STREAMINFO block */ uint32_t depth; /* this is the metadata block header of the current block we are pointing to: */ FLAC__bool is_last; FLAC__MetadataType type; uint32_t length;}; FLAC_API const char * const FLAC__Metadata_SimpleIteratorStatusString[] = { "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK", "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT", "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE", "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE", "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE", "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA", "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR", "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR", "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR", "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR", "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR", "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR", "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR"}; FLAC_API FLAC__Metadata_SimpleIterator *FLAC__metadata_simple_iterator_new(void){ FLAC__Metadata_SimpleIterator *iterator = calloc(1, sizeof(FLAC__Metadata_SimpleIterator)); if(0 != iterator) { iterator->file = 0; iterator->filename = 0; iterator->tempfile_path_prefix = 0; iterator->has_stats = false; iterator->is_writable = false; iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; iterator->first_offset = iterator->offset[0] = -1; iterator->depth = 0; } return iterator;} static void simple_iterator_free_guts_(FLAC__Metadata_SimpleIterator *iterator){ if(0 != iterator->file) { fclose(iterator->file); iterator->file = 0; if(iterator->has_stats) set_file_stats_(iterator->filename, &iterator->stats); } if(0 != iterator->filename) { free(iterator->filename); iterator->filename = 0; } if(0 != iterator->tempfile_path_prefix) { free(iterator->tempfile_path_prefix); iterator->tempfile_path_prefix = 0; }} FLAC_API void FLAC__metadata_simple_iterator_delete(FLAC__Metadata_SimpleIterator *iterator){ simple_iterator_free_guts_(iterator); free(iterator);} FLAC_API FLAC__Metadata_SimpleIteratorStatus FLAC__metadata_simple_iterator_status(FLAC__Metadata_SimpleIterator *iterator){ FLAC__Metadata_SimpleIteratorStatus status; status = iterator->status; iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; return status;} static FLAC__bool simple_iterator_prime_input_(FLAC__Metadata_SimpleIterator *iterator, FLAC__bool read_only){ uint32_t ret; if(read_only || 0 == (iterator->file = flac_fopen(iterator->filename, "r+b"))) { iterator->is_writable = false; if(read_only || errno == EACCES) { if(0 == (iterator->file = flac_fopen(iterator->filename, "rb"))) { iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE; return false; } } else { iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE; return false; } } else { iterator->is_writable = true; } ret = seek_to_first_metadata_block_(iterator->file); switch(ret) { case 0: iterator->depth = 0; iterator->first_offset = iterator->offset[iterator->depth] = ftello(iterator->file); return read_metadata_block_header_(iterator); case 1: iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; return false; case 2: iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; return false; case 3: iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE; return false; default: return false; }} FLAC_API FLAC__bool FLAC__metadata_simple_iterator_init(FLAC__Metadata_SimpleIterator *iterator, const char *filename, FLAC__bool read_only, FLAC__bool preserve_file_stats){ const char *tempfile_path_prefix = 0; /*@@@ search for comments near 'flac_rename(...)' for what it will take to finish implementing this */ simple_iterator_free_guts_(iterator); if(!read_only && preserve_file_stats) iterator->has_stats = get_file_stats_(filename, &iterator->stats); if(0 == (iterator->filename = strdup(filename))) { iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; return false; } if(0 != tempfile_path_prefix && 0 == (iterator->tempfile_path_prefix = strdup(tempfile_path_prefix))) { iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; return false; } return simple_iterator_prime_input_(iterator, read_only);} FLAC_API FLAC__bool FLAC__metadata_simple_iterator_is_writable(const FLAC__Metadata_SimpleIterator *iterator){ return iterator->is_writable;} FLAC_API FLAC__bool FLAC__metadata_simple_iterator_next(FLAC__Metadata_SimpleIterator *iterator){ if(iterator->is_last) return false; if(0 != fseeko(iterator->file, iterator->length, SEEK_CUR)) { iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; return false; } iterator->offset[iterator->depth] = ftello(iterator->file); return read_metadata_block_header_(iterator);} FLAC_API FLAC__bool FLAC__metadata_simple_iterator_prev(FLAC__Metadata_SimpleIterator *iterator){ FLAC__off_t this_offset; if(iterator->offset[iterator->depth] == iterator->first_offset) return false; if(0 != fseeko(iterator->file, iterator->first_offset, SEEK_SET)) { iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; return false; } this_offset = iterator->first_offset; if(!read_metadata_block_header_(iterator)) return false; /* we ignore any error from ftello() and catch it in fseeko() */ while(ftello(iterator->file) + (FLAC__off_t)iterator->length < iterator->offset[iterator->depth]) { if(0 != fseeko(iterator->file, iterator->length, SEEK_CUR)) { iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; return false; }Lines 1–500 of 3147. Load more
/* libFLAC - Free Lossless Audio Codec library * Copyright (C) 2001-2009 Josh Coalson * Copyright (C) 2011-2016 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of the Xiph.org Foundation nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> #include <sys/stat.h> /* for stat(), maybe chmod() */ #include "private/metadata.h" #include "FLAC/stream_decoder.h" #include "share/alloc.h" #include "share/compat.h" #include "share/macros.h" #include "share/safe_str.h" #include "private/macros.h" #include "private/memory.h" // 8bb: hide POSIX warnings #ifdef _MSC_VER #pragma warning(disable: 4996) #endif /* Alias the first (in share/alloc.h) to the second (in src/libFLAC/memory.c). */ #define safe_malloc_mul_2op_ safe_malloc_mul_2op_p /**************************************************************************** * * Local function declarations * ***************************************************************************/ static void pack_uint32_(FLAC__uint32 val, FLAC__byte *b, uint32_t bytes); static void pack_uint32_little_endian_(FLAC__uint32 val, FLAC__byte *b, uint32_t bytes); static void pack_uint64_(FLAC__uint64 val, FLAC__byte *b, uint32_t bytes); static FLAC__uint32 unpack_uint32_(FLAC__byte *b, uint32_t bytes); static FLAC__uint32 unpack_uint32_little_endian_(FLAC__byte *b, uint32_t bytes); static FLAC__uint64 unpack_uint64_(FLAC__byte *b, uint32_t bytes); static FLAC__bool read_metadata_block_header_(FLAC__Metadata_SimpleIterator *iterator); static FLAC__bool read_metadata_block_data_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block); static FLAC__bool read_metadata_block_header_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__bool *is_last, FLAC__MetadataType *type, uint32_t *length); static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata *block); static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_StreamInfo *block); static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_padding_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata_Padding *block, uint32_t block_length); static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Application *block, uint32_t block_length); static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_SeekTable *block, uint32_t block_length); static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_entry_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_VorbisComment_Entry *entry, uint32_t max_length); static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata_VorbisComment *block, uint32_t block_length); static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cuesheet_track_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_CueSheet_Track *track); static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cuesheet_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_CueSheet *block); static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_picture_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Picture *block); static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Unknown *block, uint32_t block_length); static FLAC__bool write_metadata_block_header_(FILE *file, FLAC__Metadata_SimpleIteratorStatus *status, const FLAC__StreamMetadata *block); static FLAC__bool write_metadata_block_data_(FILE *file, FLAC__Metadata_SimpleIteratorStatus *status, const FLAC__StreamMetadata *block); static FLAC__bool write_metadata_block_header_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata *block); static FLAC__bool write_metadata_block_data_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata *block); static FLAC__bool write_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_StreamInfo *block); static FLAC__bool write_metadata_block_data_padding_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Padding *block, uint32_t block_length); static FLAC__bool write_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Application *block, uint32_t block_length); static FLAC__bool write_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_SeekTable *block); static FLAC__bool write_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_VorbisComment *block); static FLAC__bool write_metadata_block_data_cuesheet_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_CueSheet *block); static FLAC__bool write_metadata_block_data_picture_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Picture *block); static FLAC__bool write_metadata_block_data_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Unknown *block, uint32_t block_length); static FLAC__bool write_metadata_block_stationary_(FLAC__Metadata_SimpleIterator *iterator, const FLAC__StreamMetadata *block); static FLAC__bool write_metadata_block_stationary_with_padding_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, uint32_t padding_length, FLAC__bool padding_is_last); static FLAC__bool rewrite_whole_file_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool append); static void simple_iterator_push_(FLAC__Metadata_SimpleIterator *iterator); static FLAC__bool simple_iterator_pop_(FLAC__Metadata_SimpleIterator *iterator); static uint32_t seek_to_first_metadata_block_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb); static uint32_t seek_to_first_metadata_block_(FILE *f); static FLAC__bool simple_iterator_copy_file_prefix_(FLAC__Metadata_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, FLAC__bool append); static FLAC__bool simple_iterator_copy_file_postfix_(FLAC__Metadata_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, int fixup_is_last_code, FLAC__off_t fixup_is_last_flag_offset, FLAC__bool backup); static FLAC__bool copy_n_bytes_from_file_(FILE *file, FILE *tempfile, FLAC__off_t bytes, FLAC__Metadata_SimpleIteratorStatus *status); static FLAC__bool copy_n_bytes_from_file_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb, FLAC__off_t bytes, FLAC__Metadata_SimpleIteratorStatus *status); static FLAC__bool copy_remaining_bytes_from_file_(FILE *file, FILE *tempfile, FLAC__Metadata_SimpleIteratorStatus *status); static FLAC__bool copy_remaining_bytes_from_file_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Eof eof_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb, FLAC__Metadata_SimpleIteratorStatus *status); static FLAC__bool open_tempfile_(const char *filename, const char *tempfile_path_prefix, FILE **tempfile, char **tempfilename, FLAC__Metadata_SimpleIteratorStatus *status); static FLAC__bool transport_tempfile_(const char *filename, FILE **tempfile, char **tempfilename, FLAC__Metadata_SimpleIteratorStatus *status); static void cleanup_tempfile_(FILE **tempfile, char **tempfilename); static FLAC__bool get_file_stats_(const char *filename, struct flac_stat_s *stats); static void set_file_stats_(const char *filename, struct flac_stat_s *stats); static int fseek_wrapper_(FLAC__IOHandle handle, FLAC__int64 offset, int whence); static FLAC__int64 ftell_wrapper_(FLAC__IOHandle handle); static FLAC__Metadata_ChainStatus get_equivalent_status_(FLAC__Metadata_SimpleIteratorStatus status); #ifdef FLAC__VALGRIND_TESTING static size_t local__fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) { size_t ret = fwrite(ptr, size, nmemb, stream); if(!ferror(stream)) fflush(stream); return ret; } #else #define local__fwrite fwrite #endif /**************************************************************************** * * Level 0 implementation * ***************************************************************************/ static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); static void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); static void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); typedef struct { FLAC__bool got_error; FLAC__StreamMetadata *object; } level0_client_data; static FLAC__StreamMetadata *get_one_metadata_block_(const char *filename, FLAC__MetadataType type) { level0_client_data cd; FLAC__StreamDecoder *decoder; cd.got_error = false; cd.object = 0; decoder = FLAC__stream_decoder_new(); if(0 == decoder) return 0; FLAC__stream_decoder_set_md5_checking(decoder, false); FLAC__stream_decoder_set_metadata_ignore_all(decoder); 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) { (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) { (void)FLAC__stream_decoder_finish(decoder); FLAC__stream_decoder_delete(decoder); if(0 != cd.object) FLAC__metadata_object_delete(cd.object); return 0; } (void)FLAC__stream_decoder_finish(decoder); FLAC__stream_decoder_delete(decoder); return cd.object; } FLAC_API FLAC__bool FLAC__metadata_get_streaminfo(const char *filename, FLAC__StreamMetadata *streaminfo) { FLAC__StreamMetadata *object; object = get_one_metadata_block_(filename, FLAC__METADATA_TYPE_STREAMINFO); if (object) { /* can just copy the contents since STREAMINFO has no internal structure */ *streaminfo = *object; FLAC__metadata_object_delete(object); return true; } else { return false; } } FLAC_API FLAC__bool FLAC__metadata_get_tags(const char *filename, FLAC__StreamMetadata **tags) { *tags = get_one_metadata_block_(filename, FLAC__METADATA_TYPE_VORBIS_COMMENT); return 0 != *tags; } FLAC_API FLAC__bool FLAC__metadata_get_cuesheet(const char *filename, FLAC__StreamMetadata **cuesheet) { *cuesheet = get_one_metadata_block_(filename, FLAC__METADATA_TYPE_CUESHEET); return 0 != *cuesheet; } FLAC__StreamDecoderWriteStatus write_callback_(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_CONTINUE; } void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) { level0_client_data *cd = (level0_client_data *)client_data; (void)decoder; /* * we assume we only get here when the one metadata block we were * looking for was passed to us */ if(!cd->got_error && 0 == cd->object) { if(0 == (cd->object = FLAC__metadata_object_clone(metadata))) cd->got_error = true; } } void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) { level0_client_data *cd = (level0_client_data *)client_data; (void)decoder; if(status != FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC) cd->got_error = true; } FLAC_API FLAC__bool FLAC__metadata_get_picture(const char *filename, FLAC__StreamMetadata **picture, FLAC__StreamMetadata_Picture_Type type, const char *mime_type, const FLAC__byte *description, uint32_t max_width, uint32_t max_height, uint32_t max_depth, uint32_t max_colors) { FLAC__Metadata_SimpleIterator *it; FLAC__uint64 max_area_seen = 0; FLAC__uint64 max_depth_seen = 0; *picture = 0; it = FLAC__metadata_simple_iterator_new(); if(0 == it) return false; if(!FLAC__metadata_simple_iterator_init(it, filename, /*read_only=*/true, /*preserve_file_stats=*/true)) { FLAC__metadata_simple_iterator_delete(it); return false; } do { if(FLAC__metadata_simple_iterator_get_block_type(it) == FLAC__METADATA_TYPE_PICTURE) { FLAC__StreamMetadata *obj = FLAC__metadata_simple_iterator_get_block(it); FLAC__uint64 area = (FLAC__uint64)obj->data.picture.width * (FLAC__uint64)obj->data.picture.height; /* check constraints */ if( (type == (FLAC__StreamMetadata_Picture_Type)(-1) || type == obj->data.picture.type) && (mime_type == 0 || !strcmp(mime_type, obj->data.picture.mime_type)) && (description == 0 || !strcmp((const char *)description, (const char *)obj->data.picture.description)) && obj->data.picture.width <= max_width && obj->data.picture.height <= max_height && obj->data.picture.depth <= max_depth && obj->data.picture.colors <= max_colors && (area > max_area_seen || (area == max_area_seen && obj->data.picture.depth > max_depth_seen)) ) { if(*picture) FLAC__metadata_object_delete(*picture); *picture = obj; max_area_seen = area; max_depth_seen = obj->data.picture.depth; } else { FLAC__metadata_object_delete(obj); } } } while(FLAC__metadata_simple_iterator_next(it)); FLAC__metadata_simple_iterator_delete(it); return (0 != *picture); } /**************************************************************************** * * Level 1 implementation * ***************************************************************************/ #define SIMPLE_ITERATOR_MAX_PUSH_DEPTH (1+4) /* 1 for initial offset, +4 for our own personal use */ struct FLAC__Metadata_SimpleIterator { FILE *file; char *filename, *tempfile_path_prefix; struct flac_stat_s stats; FLAC__bool has_stats; FLAC__bool is_writable; FLAC__Metadata_SimpleIteratorStatus status; FLAC__off_t offset[SIMPLE_ITERATOR_MAX_PUSH_DEPTH]; FLAC__off_t first_offset; /* this is the offset to the STREAMINFO block */ uint32_t depth; /* this is the metadata block header of the current block we are pointing to: */ FLAC__bool is_last; FLAC__MetadataType type; uint32_t length; }; FLAC_API const char * const FLAC__Metadata_SimpleIteratorStatusString[] = { "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK", "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT", "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE", "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE", "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE", "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA", "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR", "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR", "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR", "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR", "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR", "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR", "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR" }; FLAC_API FLAC__Metadata_SimpleIterator *FLAC__metadata_simple_iterator_new(void) { FLAC__Metadata_SimpleIterator *iterator = calloc(1, sizeof(FLAC__Metadata_SimpleIterator)); if(0 != iterator) { iterator->file = 0; iterator->filename = 0; iterator->tempfile_path_prefix = 0; iterator->has_stats = false; iterator->is_writable = false; iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; iterator->first_offset = iterator->offset[0] = -1; iterator->depth = 0; } return iterator; } static void simple_iterator_free_guts_(FLAC__Metadata_SimpleIterator *iterator) { if(0 != iterator->file) { fclose(iterator->file); iterator->file = 0; if(iterator->has_stats) set_file_stats_(iterator->filename, &iterator->stats); } if(0 != iterator->filename) { free(iterator->filename); iterator->filename = 0; } if(0 != iterator->tempfile_path_prefix) { free(iterator->tempfile_path_prefix); iterator->tempfile_path_prefix = 0; } } FLAC_API void FLAC__metadata_simple_iterator_delete(FLAC__Metadata_SimpleIterator *iterator) { simple_iterator_free_guts_(iterator); free(iterator); } FLAC_API FLAC__Metadata_SimpleIteratorStatus FLAC__metadata_simple_iterator_status(FLAC__Metadata_SimpleIterator *iterator) { FLAC__Metadata_SimpleIteratorStatus status; status = iterator->status; iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; return status; } static FLAC__bool simple_iterator_prime_input_(FLAC__Metadata_SimpleIterator *iterator, FLAC__bool read_only) { uint32_t ret; if(read_only || 0 == (iterator->file = flac_fopen(iterator->filename, "r+b"))) { iterator->is_writable = false; if(read_only || errno == EACCES) { if(0 == (iterator->file = flac_fopen(iterator->filename, "rb"))) { iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE; return false; } } else { iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE; return false; } } else { iterator->is_writable = true; } ret = seek_to_first_metadata_block_(iterator->file); switch(ret) { case 0: iterator->depth = 0; iterator->first_offset = iterator->offset[iterator->depth] = ftello(iterator->file); return read_metadata_block_header_(iterator); case 1: iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; return false; case 2: iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; return false; case 3: iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE; return false; default: return false; } } FLAC_API FLAC__bool FLAC__metadata_simple_iterator_init(FLAC__Metadata_SimpleIterator *iterator, const char *filename, FLAC__bool read_only, FLAC__bool preserve_file_stats) { const char *tempfile_path_prefix = 0; /*@@@ search for comments near 'flac_rename(...)' for what it will take to finish implementing this */ simple_iterator_free_guts_(iterator); if(!read_only && preserve_file_stats) iterator->has_stats = get_file_stats_(filename, &iterator->stats); if(0 == (iterator->filename = strdup(filename))) { iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; return false; } if(0 != tempfile_path_prefix && 0 == (iterator->tempfile_path_prefix = strdup(tempfile_path_prefix))) { iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; return false; } return simple_iterator_prime_input_(iterator, read_only); } FLAC_API FLAC__bool FLAC__metadata_simple_iterator_is_writable(const FLAC__Metadata_SimpleIterator *iterator) { return iterator->is_writable; } FLAC_API FLAC__bool FLAC__metadata_simple_iterator_next(FLAC__Metadata_SimpleIterator *iterator) { if(iterator->is_last) return false; if(0 != fseeko(iterator->file, iterator->length, SEEK_CUR)) { iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; return false; } iterator->offset[iterator->depth] = ftello(iterator->file); return read_metadata_block_header_(iterator); } FLAC_API FLAC__bool FLAC__metadata_simple_iterator_prev(FLAC__Metadata_SimpleIterator *iterator) { FLAC__off_t this_offset; if(iterator->offset[iterator->depth] == iterator->first_offset) return false; if(0 != fseeko(iterator->file, iterator->first_offset, SEEK_SET)) { iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; return false; } this_offset = iterator->first_offset; if(!read_metadata_block_header_(iterator)) return false; /* we ignore any error from ftello() and catch it in fseeko() */ while(ftello(iterator->file) + (FLAC__off_t)iterator->length < iterator->offset[iterator->depth]) { if(0 != fseeko(iterator->file, iterator->length, SEEK_CUR)) { iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; return false; }