From 45bb9887e1c19f9971b62b895c51f29de783476f Mon Sep 17 00:00:00 2001 From: Josh Coalson Date: Sat, 26 Oct 2002 04:34:16 +0000 Subject: [PATCH] add more convenience for manipulating vorbis comments --- include/FLAC/metadata.h | 67 +++++++++++++++++++++++++++- src/libFLAC/metadata_object.c | 84 ++++++++++++++++++++++++++++++++++- 2 files changed, 149 insertions(+), 2 deletions(-) diff --git a/include/FLAC/metadata.h b/include/FLAC/metadata.h index 6e27ccdd..ac41b89a 100644 --- a/include/FLAC/metadata.h +++ b/include/FLAC/metadata.h @@ -903,7 +903,9 @@ FLAC_API FLAC__bool FLAC__metadata_iterator_insert_block_after(FLAC__Metadata_It /** Create a new metadata object instance of the given type. * - * The object will be "empty"; i.e. values and data pointers will be \c 0. + * The object will be "empty"; i.e. values and data pointers will be \c 0, + * with the exception of FLAC__METADATA_TYPE_VORBIS_COMMENT, which will have + * the vendor string set (but zero comments). * * \param type Type of object to create * \retval FLAC__StreamMetadata* @@ -1229,6 +1231,69 @@ FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__Str */ FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata *object, unsigned comment_num); +/*@@@@ needs unit test still */ +/** Check if the given Vorbis comment entry's field name matches the given + * field name. + * + * \param entry A pointer to an existing Vorbis comment entry. + * \param field_name The field name to check. + * \param field_name_length The length of \a field_name, not including the + * terminating \c NULL. + * \assert + * \code entry != NULL \endcode + * \code (entry->entry != NULL && entry->length > 0) + * \retval FLAC__bool + * \c true if the field names match, else \c false + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *field_name, unsigned field_name_length); + +/*@@@@ needs unit test still */ +/** Find a Vorbis comment with the given field name. + * + * The search begins at entry number \a offset; use and offset of 0 to + * search from the beginning of the comment array. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param offset The offset into the comment array from where to start + * the search. + * \param field_name The field name of the comment to find. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \retval int + * The offset in the comment array of the first comment whose field + * name matches \a field_name, or \c -1 if no match was found. + */ +FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(FLAC__StreamMetadata *object, unsigned offset, const char *field_name); + +/*@@@@ needs unit test still */ +/** Remove first Vorbis comment matching the given field name. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param field_name The field name of comment to delete. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \retval int + * \c -1 for memory allocation error, \c 0 for no matching entries, + * \c 1 for one matching entry deleted. + */ +FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__StreamMetadata *object, const char *field_name); + +/*@@@@ needs unit test still */ +/** Remove all Vorbis comments matching the given field name. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param field_name The field name of comments to delete. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \retval int + * \c -1 for memory allocation error, \c 0 for no matching entries, + * else the number of matching entries deleted. + */ +FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entries_matching(FLAC__StreamMetadata *object, const char *field_name); + /* \} */ #ifdef __cplusplus diff --git a/src/libFLAC/metadata_object.c b/src/libFLAC/metadata_object.c index b2281a69..f086f38c 100644 --- a/src/libFLAC/metadata_object.c +++ b/src/libFLAC/metadata_object.c @@ -213,7 +213,18 @@ FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_new(FLAC__MetadataType type case FLAC__METADATA_TYPE_SEEKTABLE: break; case FLAC__METADATA_TYPE_VORBIS_COMMENT: - object->length = (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN + FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN) / 8; + { + object->data.vorbis_comment.vendor_string.length = (unsigned)strlen(FLAC__VENDOR_STRING); + if(!copy_bytes_(&object->data.vorbis_comment.vendor_string.entry, (const FLAC__byte*)FLAC__VENDOR_STRING, object->data.vorbis_comment.vendor_string.length)) { + free(object); + return 0; + } + object->length = + (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8) + + object->data.vorbis_comment.vendor_string.length + + (FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN/8) + ; + } break; default: /* double protection: */ @@ -257,6 +268,8 @@ FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_clone(const FLAC__StreamMet } break; case FLAC__METADATA_TYPE_VORBIS_COMMENT: + if(0 != to->data.vorbis_comment.vendor_string.entry) + free(to->data.vorbis_comment.vendor_string.entry); if(!copy_vcentry_(&to->data.vorbis_comment.vendor_string, &object->data.vorbis_comment.vendor_string)) { FLAC__metadata_object_delete(to); return 0; @@ -751,3 +764,72 @@ FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__Str return FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments-1); } + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *field_name, unsigned field_name_length) +{ + const FLAC__byte *eq = memchr(entry->entry, '=', entry->length); +#if defined _MSC_VER || defined __MINGW32__ +#define FLAC__STRNCASECMP strnicmp +#else +#define FLAC__STRNCASECMP strncasecmp +#endif + return (0 != eq && (unsigned)(eq-entry->entry) == field_name_length && 0 == FLAC__STRNCASECMP(field_name, entry->entry, field_name_length)); +#undef FLAC__STRNCASECMP +} + +FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(FLAC__StreamMetadata *object, unsigned offset, const char *field_name) +{ + const unsigned field_name_length = strlen(field_name); + unsigned i; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + for(i = offset; i < object->data.vorbis_comment.num_comments; i++) { + if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments + i, field_name, field_name_length)) + return (int)i; + } + + return -1; +} + +FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__StreamMetadata *object, const char *field_name) +{ + const unsigned field_name_length = strlen(field_name); + unsigned i; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + for(i = 0; i < object->data.vorbis_comment.num_comments; i++) { + if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments + i, field_name, field_name_length)) { + if(!FLAC__metadata_object_vorbiscomment_delete_comment(object, i)) + return -1; + else + return 1; + } + } + + return 0; +} + +FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entries_matching(FLAC__StreamMetadata *object, const char *field_name) +{ + FLAC__bool ok = true; + unsigned matching = 0; + const unsigned field_name_length = strlen(field_name); + int i; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + /* must delete from end to start otherwise it will interfere with our iteration */ + for(i = (int)object->data.vorbis_comment.num_comments - 1; ok && i >= 0; i--) { + if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments + i, field_name, field_name_length)) { + matching++; + ok &= FLAC__metadata_object_vorbiscomment_delete_comment(object, (unsigned)i); + } + } + + return ok? (int)matching : -1; +}