📄 metadata_iterators.c
字号:
/* libFLAC - Free Lossless Audio Codec library
* Copyright (C) 2001,2002,2003,2004,2005 Josh Coalson
*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "private/metadata.h"
#include "FLAC/assert.h"
#include "FLAC/file_decoder.h"
#ifdef max
#undef max
#endif
#define max(a,b) ((a)>(b)?(a):(b))
#ifdef min
#undef min
#endif
#define min(a,b) ((a)<(b)?(a):(b))
/****************************************************************************
*
* Local function declarations
*
***************************************************************************/
static void pack_uint32_(FLAC__uint32 val, FLAC__byte *b, unsigned bytes);
static void pack_uint32_little_endian_(FLAC__uint32 val, FLAC__byte *b, unsigned bytes);
static void pack_uint64_(FLAC__uint64 val, FLAC__byte *b, unsigned bytes);
static FLAC__uint32 unpack_uint32_(FLAC__byte *b, unsigned bytes);
static FLAC__uint32 unpack_uint32_little_endian_(FLAC__byte *b, unsigned bytes);
static FLAC__uint64 unpack_uint64_(FLAC__byte *b, unsigned 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, unsigned *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, unsigned block_length);
static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Application *block, unsigned block_length);
static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_SeekTable *block, unsigned 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);
static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_VorbisComment *block);
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_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Unknown *block, unsigned 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, unsigned block_length);
static FLAC__bool write_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Application *block, unsigned 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_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Unknown *block, unsigned 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, unsigned 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 unsigned seek_to_first_metadata_block_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb);
static unsigned 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, long fixup_is_last_flag_offset, FLAC__bool backup);
static FLAC__bool copy_n_bytes_from_file_(FILE *file, FILE *tempfile, unsigned bytes/*@@@ 4G limit*/, 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, unsigned bytes/*@@@ 4G limit*/, 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 stat *stats);
static void set_file_stats_(const char *filename, struct stat *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 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;
FLAC__bool is_writable;
FLAC__Metadata_SimpleIteratorStatus status;
/*@@@ 2G limits here because of the offset type: */
long offset[SIMPLE_ITERATOR_MAX_PUSH_DEPTH];
long first_offset; /* this is the offset to the STREAMINFO block */
unsigned depth;
/* this is the metadata block header of the current block we are pointing to: */
FLAC__bool is_last;
FLAC__MetadataType type;
unsigned 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()
{
FLAC__Metadata_SimpleIterator *iterator = (FLAC__Metadata_SimpleIterator*)calloc(1, sizeof(FLAC__Metadata_SimpleIterator));
if(0 != iterator) {
iterator->file = 0;
iterator->filename = 0;
iterator->tempfile_path_prefix = 0;
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)
{
FLAC__ASSERT(0 != iterator);
if(0 != iterator->file) {
fclose(iterator->file);
iterator->file = 0;
}
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)
{
FLAC__ASSERT(0 != 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;
FLAC__ASSERT(0 != iterator);
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)
{
unsigned ret;
FLAC__ASSERT(0 != iterator);
if(read_only || 0 == (iterator->file = fopen(iterator->filename, "r+b"))) {
iterator->is_writable = false;
if(read_only) {
if(0 == (iterator->file = 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] = ftell(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:
FLAC__ASSERT(0);
return false;
}
}
#if 0
@@@ If we decide to finish implementing this, put this comment back in metadata.h
/*
* The 'tempfile_path_prefix' allows you to specify a directory where
* tempfiles should go. Remember that if your metadata edits cause the
* FLAC file to grow, the entire file will have to be rewritten. If
* 'tempfile_path_prefix' is NULL, the temp file will be written in the
* same directory as the original FLAC file. This makes replacing the
* original with the tempfile fast but requires extra space in the same
* partition for the tempfile. If space is a problem, you can pass a
* directory name belonging to a different partition in
* 'tempfile_path_prefix'. Note that you should use the forward slash
* '/' as the directory separator. A trailing slash is not needed; it
* will be added automatically.
*/
FLAC__bool FLAC__metadata_simple_iterator_init(FLAC__Metadata_SimpleIterator *iterator, const char *filename, FLAC__bool preserve_file_stats, const char *tempfile_path_prefix);
#endif
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 'rename(...)' for what it will take to finish implementing this */
FLAC__ASSERT(0 != iterator);
FLAC__ASSERT(0 != filename);
simple_iterator_free_guts_(iterator);
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)
{
FLAC__ASSERT(0 != iterator);
FLAC__ASSERT(0 != iterator->file);
return iterator->is_writable;
}
FLAC_API FLAC__bool FLAC__metadata_simple_iterator_next(FLAC__Metadata_SimpleIterator *iterator)
{
FLAC__ASSERT(0 != iterator);
FLAC__ASSERT(0 != iterator->file);
if(iterator->is_last)
return false;
if(0 != fseek(iterator->file, iterator->length, SEEK_CUR)) {
iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
return false;
}
iterator->offset[iterator->depth] = ftell(iterator->file);
return read_metadata_block_header_(iterator);
}
FLAC_API FLAC__bool FLAC__metadata_simple_iterator_prev(FLAC__Metadata_SimpleIterator *iterator)
{
long this_offset;
FLAC__ASSERT(0 != iterator);
FLAC__ASSERT(0 != iterator->file);
if(iterator->offset[iterator->depth] == iterator->first_offset)
return false;
if(0 != fseek(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 ftell() and catch it in fseek() */
while(ftell(iterator->file) + (long)iterator->length < iterator->offset[iterator->depth]) {
if(0 != fseek(iterator->file, iterator->length, SEEK_CUR)) {
iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
return false;
}
this_offset = ftell(iterator->file);
if(!read_metadata_block_header_(iterator))
return false;
}
iterator->offset[iterator->depth] = this_offset;
return true;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -