From def597ee59e41e6ac9a170dead3e1f8300135689 Mon Sep 17 00:00:00 2001 From: Josh Coalson Date: Thu, 30 Dec 2004 00:59:30 +0000 Subject: [PATCH] additions to metadata object api: more vorbiscomment functions, trailing-null on vorbis comment field values enforced everywhere --- doc/html/changelog.html | 89 +++++ include/FLAC++/metadata.h | 25 +- include/FLAC/format.h | 9 + include/FLAC/metadata.h | 191 ++++++++--- src/flac/vorbiscomment.c | 2 +- src/libFLAC++/metadata.cpp | 50 ++- src/libFLAC/metadata_iterators.c | 4 +- src/libFLAC/metadata_object.c | 177 ++++++++-- src/libFLAC/stream_decoder.c | 6 +- src/metaflac/operations_shorthand_vorbiscomment.c | 2 +- src/metaflac/utils.c | 20 +- src/share/grabbag/replaygain.c | 2 +- src/test_libFLAC++/metadata_manip.cpp | 4 +- src/test_libFLAC++/metadata_object.cpp | 94 ++++- src/test_libFLAC++/metadata_utils.c | 8 +- src/test_libFLAC/metadata_manip.c | 4 +- src/test_libFLAC/metadata_object.c | 395 +++++++++++++++++++++- src/test_libFLAC/metadata_utils.c | 8 +- src/test_libOggFLAC++/metadata_utils.c | 8 +- src/test_libOggFLAC/metadata_utils.c | 8 +- 20 files changed, 984 insertions(+), 122 deletions(-) diff --git a/doc/html/changelog.html b/doc/html/changelog.html index 3b5cf76..77168cb 100644 --- a/doc/html/changelog.html +++ b/doc/html/changelog.html @@ -85,6 +85,95 @@

+ @@@@@@ +

+

+

+

+ +

FLAC 1.1.1

diff --git a/include/FLAC++/metadata.h b/include/FLAC++/metadata.h index d7e91a6..138580e 100644 --- a/include/FLAC++/metadata.h +++ b/include/FLAC++/metadata.h @@ -492,9 +492,10 @@ namespace FLAC { * name is undefined; only the field value is relevant. * * A \a field as used in the methods refers to an - * entire 'NAME=VALUE' string; the string is not null- - * terminated and a length field is required since the - * string may contain embedded nulls. + * entire 'NAME=VALUE' string; for convenience the + * string is null-terminated. A length field is + * required in the unlikely event that the value + * contains contain embedded nulls. * * A \a field_name is what is on the left side of the * first '=' in the \a field. By definition it is ASCII @@ -505,7 +506,10 @@ namespace FLAC { * A \a field_value is what is on the right side of the * first '=' in the \a field. By definition, this may * contain embedded nulls and so a \a field_value_length - * is requires to describe it. + * is required to describe it. However in practice, + * embedded nulls are not known to be used, so it is + * generally safe to treat field values as null- + * terminated UTF-8 strings. * * Always check is_valid() after the constructor or operator= * to make sure memory was properly allocated. @@ -513,9 +517,15 @@ namespace FLAC { class FLACPP_API Entry { public: Entry(); + Entry(const char *field, unsigned field_length); + Entry(const char *field); // assumes \a field is null-terminated + Entry(const char *field_name, const char *field_value, unsigned field_value_length); + Entry(const char *field_name, const char *field_value); // assumes \a field_value is null-terminated + Entry(const Entry &entry); + void operator=(const Entry &entry); virtual ~Entry(); @@ -532,8 +542,10 @@ namespace FLAC { const char *get_field_value() const; bool set_field(const char *field, unsigned field_length); + bool set_field(const char *field); // assumes \a field is null-terminated bool set_field_name(const char *field_name); bool set_field_value(const char *field_value, unsigned field_value_length); + bool set_field_value(const char *field_value); // assumes \a field_value is null-terminated protected: bool is_valid_; ::FLAC__StreamMetadata_VorbisComment_Entry entry_; @@ -548,7 +560,9 @@ namespace FLAC { void clear_field_name(); void clear_field_value(); void construct(const char *field, unsigned field_length); + void construct(const char *field); // assumes \a field is null-terminated void construct(const char *field_name, const char *field_value, unsigned field_value_length); + void construct(const char *field_name, const char *field_value); // assumes \a field_value is null-terminated void compose_field(); void parse_field(); }; @@ -606,6 +620,9 @@ namespace FLAC { //! See FLAC__metadata_object_vorbiscomment_insert_comment() bool insert_comment(unsigned index, const Entry &entry); + //! See FLAC__metadata_object_vorbiscomment_append_comment() + bool append_comment(const Entry &entry); + //! See FLAC__metadata_object_vorbiscomment_delete_comment() bool delete_comment(unsigned index); }; diff --git a/include/FLAC/format.h b/include/FLAC/format.h index e003c34..d4e6bca 100644 --- a/include/FLAC/format.h +++ b/include/FLAC/format.h @@ -583,6 +583,15 @@ typedef struct { /** Vorbis comment entry structure used in VORBIS_COMMENT blocks. (c.f. format specification) + * + * For convenience, the APIs maintain a trailing NUL character at the end of + * \a entry which is not counted toward \a length or stored in the stream, + * i.e. + * \code strlen(entry) == length \endcode + * + * It's recommended but not required for users to follow this convention as + * well when dealing directly with FLAC__StreamMetadata_VorbisComment_Entry + * as it makes dealing with plain strings easier. */ typedef struct { FLAC__uint32 length; diff --git a/include/FLAC/metadata.h b/include/FLAC/metadata.h index bd80821..76893af 100644 --- a/include/FLAC/metadata.h +++ b/include/FLAC/metadata.h @@ -1087,6 +1087,13 @@ FLAC_API FLAC__bool FLAC__metadata_iterator_insert_block_after(FLAC__Metadata_It * FLAC__metadata_object_application_set_data(), you will get an assertion * failure. * + * The FLAC__metadata_object_vorbiscomment_*() functions for convenience + * maintain a trailing NUL on each Vorbis comment entry. This is not counted + * toward the length or stored in the stream, but it can make working with plain + * comments (those that don't contain embedded-NULs in the value) easier. + * Entries passed into these functions have trailing NULs added if missing, and + * returned entries are guaranteed to have a trailing NUL. + * * There is no need to recalculate the length field on metadata blocks you * have modified. They will be calculated automatically before they are * written back to a file. @@ -1155,8 +1162,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_is_equal(const FLAC__StreamMetadata *b /** Sets the application data of an APPLICATION block. * * If \a copy is \c true, a copy of the data is stored; otherwise, the object - * takes ownership of the pointer. Returns \c false if \a copy == \c true - * and malloc fails. + * takes ownership of the pointer. * * \param object A pointer to an existing APPLICATION object. * \param data A pointer to the data to set. @@ -1168,7 +1174,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_is_equal(const FLAC__StreamMetadata *b * \code (data != NULL && length > 0) || * (data == NULL && length == 0 && copy == false) \endcode * \retval FLAC__bool - * \c false if \a copy is \c true and malloc fails, else \c true. + * \c false if \a copy is \c true and malloc() fails, else \c true. */ FLAC_API FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, unsigned length, FLAC__bool copy); @@ -1327,15 +1333,20 @@ FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_point * \code object != NULL \endcode * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode * \retval FLAC__bool - * \c false if realloc fails, else \c true. + * \c false if realloc() fails, else \c true. */ FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_sort(FLAC__StreamMetadata *object, FLAC__bool compact); /** Sets the vendor string in a VORBIS_COMMENT block. * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * * If \a copy is \c true, a copy of the entry is stored; otherwise, the object - * takes ownership of the \c entry->entry pointer. Returns \c false if - * \a copy == \c true and malloc fails. + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. * * \param object A pointer to an existing VORBIS_COMMENT object. * \param entry The entry to set the vendor string to. @@ -1343,10 +1354,10 @@ FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_sort(FLAC__StreamMe * \assert * \code object != NULL \endcode * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode - * \code (entry->entry != NULL && entry->length > 0) || - * (entry->entry == NULL && entry->length == 0) \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0) \endcode * \retval FLAC__bool - * \c false if \a copy is \c true and malloc fails, else \c true. + * \c false if memory allocation fails, else \c true. */ FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); @@ -1369,9 +1380,14 @@ FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__St /** Sets a comment in a VORBIS_COMMENT block. * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * * If \a copy is \c true, a copy of the entry is stored; otherwise, the object - * takes ownership of the \c entry->entry pointer. Returns \c false if - * \a copy == \c true and malloc fails. + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. * * \param object A pointer to an existing VORBIS_COMMENT object. * \param comment_num Index into comment array to set. @@ -1381,18 +1397,23 @@ FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__St * \code object != NULL \endcode * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode * \code comment_num < object->data.vorbis_comment.num_comments \endcode - * \code (entry->entry != NULL && entry->length > 0) || - * (entry->entry == NULL && entry->length == 0) \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0) \endcode * \retval FLAC__bool - * \c false if \a copy is \c true and malloc fails, else \c true. + * \c false if memory allocation fails, else \c true. */ FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); /** Insert a comment in a VORBIS_COMMENT block at the given index. * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * * If \a copy is \c true, a copy of the entry is stored; otherwise, the object - * takes ownership of the \c entry->entry pointer. Returns \c false if - * \a copy == \c true and malloc fails. + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. * * \param object A pointer to an existing VORBIS_COMMENT object. * \param comment_num The index at which to insert the comment. The comments @@ -1405,13 +1426,71 @@ FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__Stream * \code object != NULL \endcode * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode * \code object->data.vorbis_comment.num_comments >= comment_num \endcode - * \code (entry->entry != NULL && entry->length > 0) || - * (entry->entry == NULL && entry->length == 0 && copy == false) \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0 && copy == false) \endcode * \retval FLAC__bool - * \c false if \a copy is \c true and malloc fails, else \c true. + * \c false if memory allocation fails, else \c true. */ FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); +/** Appends a comment to a VORBIS_COMMENT block. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param entry The comment to insert. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_append_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); + +/** Replaces comments in a VORBIS_COMMENT block with a new one. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * Depending on the the value of \a all, either all or just the first comment + * whose field name(s) match the given entry's name will be replaced by the + * given entry. If no comments match, \a entry will simply be appended. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param entry The comment to insert. + * \param all If \c true, all comments whose field name matches + * \a entry's field name will be removed, and \a entry will + * be inserted at the position of the first matching + * comment. If \c false, only the first comment whose + * field name matches \a entry's field name will be + * replaced with \a entry. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_replace_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool all, FLAC__bool copy); + /** Delete a comment in a VORBIS_COMMENT block at the given index. * * \param object A pointer to an existing VORBIS_COMMENT object. @@ -1420,14 +1499,51 @@ FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__Str * \code object != NULL \endcode * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode * \code object->data.vorbis_comment.num_comments > comment_num \endcode - * \code (entry->entry != NULL && entry->length > 0) || - * (entry->entry == NULL && entry->length == 0 && copy == false) \endcode * \retval FLAC__bool - * \c false if realloc fails, else \c true. + * \c false if realloc() fails, else \c true. */ FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata *object, unsigned comment_num); -/*@@@@ add to unit tests */ +/** Creates a Vorbis comment entry from NUL-terminated name and value strings. + * + * On return, the filled-in \a entry->entry pointer will point to malloc()ed + * memory and shall be owned by the caller. For convenience the entry will + * have a terminating NUL. + * + * \param entry A pointer to a Vorbis comment entry. The entry's + * \c entry pointer should not point to allocated + * memory as it will be overwritten. + * \param field_name The field name in ASCII, \c NULL terminated. + * \param field_value The field value in UTF-8, \c NULL terminated. + * \assert + * \code entry != NULL \endcode + * \code field_name != NULL \endcode + * \code field_value != NULL \endcode + * \retval FLAC__bool + * \c false if malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *field_name, const char *field_value); + +/** Splits a Vorbis comment entry into NUL-terminated name and value strings. + * + * The returned pointers to name and value will be allocated by malloc() + * and shall be owned by the caller. + * + * \param entry A pointer to an existing Vorbis comment entry. + * \param field_name The address of where the returned pointer to the + * field name will be stored. + * \param field_value The address of where the returned pointer to the + * field value will be stored. + * \assert + * \code (entry.entry != NULL && entry.length > 0) \endcode + * \code memchr(entry.entry, '=', entry.length) != NULL \endcode + * \code field_name != NULL \endcode + * \code field_value != NULL \endcode + * \retval FLAC__bool + * \c false if malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(FLAC__StreamMetadata_VorbisComment_Entry entry, char **field_name, char **field_value); + /** Check if the given Vorbis comment entry's field name matches the given * field name. * @@ -1436,14 +1552,12 @@ FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__Str * \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) \endcode + * \code (entry.entry != NULL && entry.length > 0) \endcode * \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); +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(FLAC__StreamMetadata_VorbisComment_Entry entry, const char *field_name, unsigned field_name_length); -/*@@@@ add to unit tests */ /** Find a Vorbis comment with the given field name. * * The search begins at entry number \a offset; use an offset of 0 to @@ -1456,13 +1570,13 @@ FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC * \assert * \code object != NULL \endcode * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code field_name != NULL \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(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name); -/*@@@@ add to unit tests */ /** Remove first Vorbis comment matching the given field name. * * \param object A pointer to an existing VORBIS_COMMENT object. @@ -1476,7 +1590,6 @@ FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(const FLAC__Str */ FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__StreamMetadata *object, const char *field_name); -/*@@@@ add to unit tests */ /** Remove all Vorbis comments matching the given field name. * * \param object A pointer to an existing VORBIS_COMMENT object. @@ -1561,7 +1674,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_resize_indices(FLAC__St * \code object->data.cue_sheet.num_tracks > track_num \endcode * \code object->data.cue_sheet.tracks[track_num].num_indices >= index_num \endcode * \retval FLAC__bool - * \c false if realloc fails, else \c true. + * \c false if realloc() fails, else \c true. */ FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num, FLAC__StreamMetadata_CueSheet_Index index); @@ -1585,7 +1698,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_index(FLAC__Stre * \code object->data.cue_sheet.num_tracks > track_num \endcode * \code object->data.cue_sheet.tracks[track_num].num_indices >= index_num \endcode * \retval FLAC__bool - * \c false if realloc fails, else \c true. + * \c false if realloc() fails, else \c true. */ FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_blank_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num); @@ -1604,7 +1717,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_blank_index(FLAC * \code object->data.cue_sheet.num_tracks > track_num \endcode * \code object->data.cue_sheet.tracks[track_num].num_indices > index_num \endcode * \retval FLAC__bool - * \c false if realloc fails, else \c true. + * \c false if realloc() fails, else \c true. */ FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_delete_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num); @@ -1628,8 +1741,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMet /** Sets a track in a CUESHEET block. * * If \a copy is \c true, a copy of the track is stored; otherwise, the object - * takes ownership of the \a track pointer. Returns \c false if - * \a copy == \c true and malloc fails. + * takes ownership of the \a track pointer. * * \param object A pointer to an existing CUESHEET object. * \param track_num Index into track array to set. NOTE: this is not @@ -1644,15 +1756,14 @@ FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMet * \code (track->indices != NULL && track->num_indices > 0) || * (track->indices == NULL && track->num_indices == 0) * \retval FLAC__bool - * \c false if \a copy is \c true and malloc fails, else \c true. + * \c false if \a copy is \c true and malloc() fails, else \c true. */ FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy); /** Insert a track in a CUESHEET block at the given index. * * If \a copy is \c true, a copy of the track is stored; otherwise, the object - * takes ownership of the \a track pointer. Returns \c false if - * \a copy == \c true and malloc fails. + * takes ownership of the \a track pointer. * * \param object A pointer to an existing CUESHEET object. * \param track_num The index at which to insert the track. NOTE: this @@ -1668,7 +1779,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadat * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode * \code object->data.cue_sheet.num_tracks >= track_num \endcode * \retval FLAC__bool - * \c false if \a copy is \c true and malloc fails, else \c true. + * \c false if \a copy is \c true and malloc() fails, else \c true. */ FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy); @@ -1687,7 +1798,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMeta * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode * \code object->data.cue_sheet.num_tracks >= track_num \endcode * \retval FLAC__bool - * \c false if \a copy is \c true and malloc fails, else \c true. + * \c false if \a copy is \c true and malloc() fails, else \c true. */ FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_blank_track(FLAC__StreamMetadata *object, unsigned track_num); @@ -1702,7 +1813,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_blank_track(FLAC__Stre * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode * \code object->data.cue_sheet.num_tracks > track_num \endcode * \retval FLAC__bool - * \c false if realloc fails, else \c true. + * \c false if realloc() fails, else \c true. */ FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_delete_track(FLAC__StreamMetadata *object, unsigned track_num); diff --git a/src/flac/vorbiscomment.c b/src/flac/vorbiscomment.c index 15e94e9..2749521 100644 --- a/src/flac/vorbiscomment.c +++ b/src/flac/vorbiscomment.c @@ -122,7 +122,7 @@ static FLAC__bool set_vc_field(FLAC__StreamMetadata *block, const Argument_VcFie entry.length = strlen((const char *)entry.entry); - if(!FLAC__metadata_object_vorbiscomment_insert_comment(block, block->data.vorbis_comment.num_comments, entry, /*copy=*/true)) { + if(!FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/true)) { if(needs_free) free(converted); *violation = "memory allocation failure"; diff --git a/src/libFLAC++/metadata.cpp b/src/libFLAC++/metadata.cpp index 0a9cd6e..871b01e 100644 --- a/src/libFLAC++/metadata.cpp +++ b/src/libFLAC++/metadata.cpp @@ -461,12 +461,24 @@ namespace FLAC { construct(field, field_length); } + VorbisComment::Entry::Entry(const char *field) + { + zero(); + construct(field); + } + VorbisComment::Entry::Entry(const char *field_name, const char *field_value, unsigned field_value_length) { zero(); construct(field_name, field_value, field_value_length); } + VorbisComment::Entry::Entry(const char *field_name, const char *field_value) + { + zero(); + construct(field_name, field_value); + } + VorbisComment::Entry::Entry(const Entry &entry) { FLAC__ASSERT(entry.is_valid()); @@ -540,18 +552,24 @@ namespace FLAC { clear_entry(); - if(0 == (entry_.entry = (FLAC__byte*)malloc(field_length))) { + if(0 == (entry_.entry = (FLAC__byte*)malloc(field_length+1))) { is_valid_ = false; } else { entry_.length = field_length; memcpy(entry_.entry, field, field_length); + entry_.entry[field_length] = '\0'; (void) parse_field(); } return is_valid_; } + bool VorbisComment::Entry::set_field(const char *field) + { + return set_field(field, strlen(field)); + } + bool VorbisComment::Entry::set_field_name(const char *field_name) { FLAC__ASSERT(is_valid()); @@ -577,18 +595,24 @@ namespace FLAC { clear_field_value(); - if(0 == (field_value_ = (char *)malloc(field_value_length))) { + if(0 == (field_value_ = (char *)malloc(field_value_length+1))) { is_valid_ = false; } else { field_value_length_ = field_value_length; memcpy(field_value_, field_value, field_value_length); + field_value_[field_value_length] = '\0'; compose_field(); } return is_valid_; } + bool VorbisComment::Entry::set_field_value(const char *field_value) + { + return set_field_value(field_value, strlen(field_value)); + } + void VorbisComment::Entry::zero() { is_valid_ = true; @@ -641,17 +665,27 @@ namespace FLAC { parse_field(); } + void VorbisComment::Entry::construct(const char *field) + { + construct(field, strlen(field)); + } + void VorbisComment::Entry::construct(const char *field_name, const char *field_value, unsigned field_value_length) { if(set_field_name(field_name) && set_field_value(field_value, field_value_length)) compose_field(); } + void VorbisComment::Entry::construct(const char *field_name, const char *field_value) + { + construct(field_name, field_value, strlen(field_value)); + } + void VorbisComment::Entry::compose_field() { clear_entry(); - if(0 == (entry_.entry = (FLAC__byte*)malloc(field_name_length_ + 1 + field_value_length_))) { + if(0 == (entry_.entry = (FLAC__byte*)malloc(field_name_length_ + 1 + field_value_length_ + 1))) { is_valid_ = false; } else { @@ -661,6 +695,7 @@ namespace FLAC { entry_.length += 1; memcpy(entry_.entry + entry_.length, field_value_, field_value_length_); entry_.length += field_value_length_; + entry_.entry[entry_.length] = '\0'; is_valid_ = true; } } @@ -692,11 +727,12 @@ namespace FLAC { } else { field_value_length_ = entry_.length - field_name_length_ - 1; - if(0 == (field_value_ = (char *)malloc(field_value_length_))) { + if(0 == (field_value_ = (char *)malloc(field_value_length_ + 1))) { // +1 for the trailing \0 is_valid_ = false; return; } memcpy(field_value_, ++p, field_value_length_); + field_value_[field_value_length_] = '\0'; } is_valid_ = true; @@ -757,6 +793,12 @@ namespace FLAC { return (bool)::FLAC__metadata_object_vorbiscomment_insert_comment(object_, index, entry.get_entry(), /*copy=*/true); } + bool VorbisComment::append_comment(const VorbisComment::Entry &entry) + { + FLAC__ASSERT(is_valid()); + return (bool)::FLAC__metadata_object_vorbiscomment_append_comment(object_, entry.get_entry(), /*copy=*/true); + } + bool VorbisComment::delete_comment(unsigned index) { FLAC__ASSERT(is_valid()); diff --git a/src/libFLAC/metadata_iterators.c b/src/libFLAC/metadata_iterators.c index 45e5c3f..13f1c3f 100644 --- a/src/libFLAC/metadata_iterators.c +++ b/src/libFLAC/metadata_iterators.c @@ -1982,11 +1982,13 @@ FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_entr entry->entry = 0; } else { - if(0 == (entry->entry = (FLAC__byte*)malloc(entry->length))) + if(0 == (entry->entry = (FLAC__byte*)malloc(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_READ_ERROR; + + entry->entry[entry->length] = '\0'; } return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; diff --git a/src/libFLAC/metadata_object.c b/src/libFLAC/metadata_object.c index 589357b..3a613a1 100644 --- a/src/libFLAC/metadata_object.c +++ b/src/libFLAC/metadata_object.c @@ -60,6 +60,18 @@ static FLAC__bool copy_bytes_(FLAC__byte **to, const FLAC__byte *from, unsigned return true; } +static FLAC__bool ensure_null_terminated_(FLAC__byte **entry, unsigned length) +{ + FLAC__byte *x = (FLAC__byte*)realloc(*entry, length+1); + if(0 != x) { + x[length] = '\0'; + *entry = x; + return true; + } + else + return false; +} + static FLAC__bool copy_vcentry_(FLAC__StreamMetadata_VorbisComment_Entry *to, const FLAC__StreamMetadata_VorbisComment_Entry *from) { to->length = from->length; @@ -70,9 +82,10 @@ static FLAC__bool copy_vcentry_(FLAC__StreamMetadata_VorbisComment_Entry *to, co else { FLAC__byte *x; FLAC__ASSERT(from->length > 0); - if(0 == (x = (FLAC__byte*)malloc(from->length))) + if(0 == (x = (FLAC__byte*)malloc(from->length+1))) return false; memcpy(x, from->entry, from->length); + x[from->length] = '\0'; to->entry = x; } return true; @@ -194,13 +207,30 @@ static FLAC__bool vorbiscomment_set_entry_(FLAC__StreamMetadata *object, FLAC__S save = dest->entry; - /* do the copy first so that if we fail we leave the object untouched */ - if(copy && (0 != src->entry && src->length > 0)) { - if(!copy_vcentry_(dest, src)) - return false; + if(0 != src->entry && src->length > 0) { + if(copy) { + /* do the copy first so that if we fail we leave the dest object untouched */ + if(!copy_vcentry_(dest, src)) + return false; + } + else { + /* we have to make sure that the string we're taking over is null-terminated */ + + /* + * Stripping the const from src->entry is OK since we're taking + * ownership of the pointer. This is a hack around a deficiency + * in the API where the same function is used for 'copy' and + * 'own', but the source entry is a const pointer. If we were + * precise, the 'own' flavor would be a separate function with a + * non-const source pointer. But it's not, so we hack away. + */ + if(!ensure_null_terminated_((FLAC__byte**)(&src->entry), src->length)) + return false; + *dest = *src; + } } else { - /* either we're not copying or the src is null */ + /* the src is null */ *dest = *src; } @@ -211,6 +241,22 @@ static FLAC__bool vorbiscomment_set_entry_(FLAC__StreamMetadata *object, FLAC__S return true; } +static int vorbiscomment_find_entry_from_(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name, unsigned field_name_length) +{ + unsigned i; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + FLAC__ASSERT(0 != field_name); + + 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; +} + static void cuesheet_calculate_length_(FLAC__StreamMetadata *object) { unsigned i; @@ -369,7 +415,7 @@ FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_new(FLAC__MetadataType type case FLAC__METADATA_TYPE_VORBIS_COMMENT: { 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)) { + if(!copy_bytes_(&object->data.vorbis_comment.vendor_string.entry, (const FLAC__byte*)FLAC__VENDOR_STRING, object->data.vorbis_comment.vendor_string.length+1)) { free(object); return 0; } @@ -994,6 +1040,49 @@ FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__Str return FLAC__metadata_object_vorbiscomment_set_comment(object, comment_num, entry, copy); } +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_append_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + return FLAC__metadata_object_vorbiscomment_insert_comment(object, object->data.vorbis_comment.num_comments, entry, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_replace_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool all, FLAC__bool copy) +{ + FLAC__ASSERT(0 != entry.entry && entry.length > 0); + { + int i; + unsigned field_name_length; + const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length); + + FLAC__ASSERT(0 != eq); + + if(0 == eq) + return false; /* double protection */ + + field_name_length = eq-entry.entry; + + if((i = vorbiscomment_find_entry_from_(object, 0, entry.entry, field_name_length)) >= 0) { + unsigned index = (unsigned)i; + if(!FLAC__metadata_object_vorbiscomment_set_comment(object, index, entry, copy)) + return false; + if(all && (index+1 < object->data.vorbis_comment.num_comments)) { + for(i = vorbiscomment_find_entry_from_(object, index+1, entry.entry, field_name_length); i >= 0; ) { + if(!FLAC__metadata_object_vorbiscomment_delete_comment(object, (unsigned)i)) + return false; + if((unsigned)i < object->data.vorbis_comment.num_comments) + i = vorbiscomment_find_entry_from_(object, (unsigned)i, entry.entry, field_name_length); + else + i = -1; + } + } + return true; + } + else + return FLAC__metadata_object_vorbiscomment_append_comment(object, entry, copy); + } +} + FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata *object, unsigned comment_num) { FLAC__StreamMetadata_VorbisComment *vc; @@ -1016,32 +1105,74 @@ 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) +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *field_name, const char *field_value) { - const FLAC__byte *eq = (FLAC__byte*)memchr(entry->entry, '=', entry->length); + FLAC__ASSERT(0 != entry); + FLAC__ASSERT(0 != field_name); + FLAC__ASSERT(0 != field_value); + + { + const size_t nn = strlen(field_name); + const size_t nv = strlen(field_value); + entry->length = nn + 1 /*=*/ + nv; + if(0 == (entry->entry = malloc(entry->length+1))) + return false; + memcpy(entry->entry, field_name, nn); + entry->entry[nn] = '='; + memcpy(entry->entry+nn+1, field_value, nv); + entry->entry[entry->length] = '\0'; + } + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(const FLAC__StreamMetadata_VorbisComment_Entry entry, char **field_name, char **field_value) +{ + FLAC__ASSERT(0 != entry.entry && entry.length > 0); + FLAC__ASSERT(0 != field_name); + FLAC__ASSERT(0 != field_value); + { + const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length); + const size_t nn = eq-entry.entry; + const size_t nv = entry.length-nn-1; /* -1 for the '=' */ + FLAC__ASSERT(0 != eq); + if(0 == eq) + return false; /* double protection */ + if(0 == (*field_name = malloc(nn+1))) + return false; + if(0 == (*field_value = malloc(nv+1))) { + free(*field_name); + return false; + } + memcpy(*field_name, entry.entry, nn); + memcpy(*field_value, entry.entry+nn+1, nv); + (*field_name)[nn] = '\0'; + (*field_value)[nv] = '\0'; + } + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(FLAC__StreamMetadata_VorbisComment_Entry entry, const char *field_name, unsigned field_name_length) +{ + FLAC__ASSERT(0 != entry.entry && entry.length > 0); + { + const FLAC__byte *eq = (FLAC__byte*)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, (const char *)entry->entry, field_name_length)); + return (0 != eq && (unsigned)(eq-entry.entry) == field_name_length && 0 == FLAC__STRNCASECMP(field_name, (const char *)entry.entry, field_name_length)); #undef FLAC__STRNCASECMP + } } FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name) { - const unsigned field_name_length = strlen(field_name); - unsigned i; + FLAC__ASSERT(0 != field_name); - 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; + return vorbiscomment_find_entry_from_(object, offset, field_name, strlen(field_name)); } FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__StreamMetadata *object, const char *field_name) @@ -1053,7 +1184,7 @@ FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__Str 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_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 @@ -1076,7 +1207,7 @@ FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entries_matching(FLAC__S /* 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)) { + 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); } diff --git a/src/libFLAC/stream_decoder.c b/src/libFLAC/stream_decoder.c index dc68710..5995afe 100644 --- a/src/libFLAC/stream_decoder.c +++ b/src/libFLAC/stream_decoder.c @@ -1167,12 +1167,13 @@ FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__Stre if(!FLAC__bitbuffer_read_raw_uint32_little_endian(decoder->private_->input, &obj->vendor_string.length, read_callback_, decoder)) return false; /* the read_callback_ sets the state for us */ if(obj->vendor_string.length > 0) { - if(0 == (obj->vendor_string.entry = (FLAC__byte*)malloc(obj->vendor_string.length))) { + if(0 == (obj->vendor_string.entry = (FLAC__byte*)malloc(obj->vendor_string.length+1))) { decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; return false; } if(!FLAC__bitbuffer_read_byte_block_aligned_no_crc(decoder->private_->input, obj->vendor_string.entry, obj->vendor_string.length, read_callback_, decoder)) return false; /* the read_callback_ sets the state for us */ + obj->vendor_string.entry[obj->vendor_string.length] = '\0'; } else obj->vendor_string.entry = 0; @@ -1193,12 +1194,13 @@ FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__Stre if(!FLAC__bitbuffer_read_raw_uint32_little_endian(decoder->private_->input, &obj->comments[i].length, read_callback_, decoder)) return false; /* the read_callback_ sets the state for us */ if(obj->comments[i].length > 0) { - if(0 == (obj->comments[i].entry = (FLAC__byte*)malloc(obj->comments[i].length))) { + if(0 == (obj->comments[i].entry = (FLAC__byte*)malloc(obj->comments[i].length+1))) { decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; return false; } if(!FLAC__bitbuffer_read_byte_block_aligned_no_crc(decoder->private_->input, obj->comments[i].entry, obj->comments[i].length, read_callback_, decoder)) return false; /* the read_callback_ sets the state for us */ + obj->comments[i].entry[obj->comments[i].length] = '\0'; } else obj->comments[i].entry = 0; diff --git a/src/metaflac/operations_shorthand_vorbiscomment.c b/src/metaflac/operations_shorthand_vorbiscomment.c index 6f14681..f92d879 100644 --- a/src/metaflac/operations_shorthand_vorbiscomment.c +++ b/src/metaflac/operations_shorthand_vorbiscomment.c @@ -192,7 +192,7 @@ FLAC__bool set_vc_field(const char *filename, FLAC__StreamMetadata *block, const entry.length = strlen((const char *)entry.entry); - if(!FLAC__metadata_object_vorbiscomment_insert_comment(block, block->data.vorbis_comment.num_comments, entry, /*copy=*/true)) { + if(!FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/true)) { if(needs_free) free(converted); fprintf(stderr, "%s: ERROR: memory allocation failure\n", filename); diff --git a/src/metaflac/utils.c b/src/metaflac/utils.c index 9983d9b..745e70f 100644 --- a/src/metaflac/utils.c +++ b/src/metaflac/utils.c @@ -220,24 +220,16 @@ void write_vc_field(const char *filename, const FLAC__StreamMetadata_VorbisComme if(!raw) { /* - * utf8_decode() works on NULL-terminated strings, so - * we append a null to the entry. @@@ Note, this means - * that comments that contain an embedded null will be - * truncated by utf_decode(). + * WATCHOUT: comments that contain an embedded null will + * be truncated by utf_decode(). */ - char *terminated, *converted; + char *converted; - if(0 == (terminated = malloc(entry->length + 1))) - die("out of memory allocating space for vorbis comment"); - memcpy(terminated, entry->entry, entry->length); - terminated[entry->length] = '\0'; - if(utf8_decode(terminated, &converted) >= 0) { + if(utf8_decode(entry->entry, &converted) >= 0) { (void) local_fwrite(converted, 1, strlen(converted), f); - free(terminated); free(converted); } else { - free(terminated); (void) local_fwrite(entry->entry, 1, entry->length, f); } } @@ -246,7 +238,7 @@ void write_vc_field(const char *filename, const FLAC__StreamMetadata_VorbisComme } } - fprintf(f, "\n"); + putc('\n', f); } void write_vc_fields(const char *filename, const char *field_name, const FLAC__StreamMetadata_VorbisComment_Entry entry[], unsigned num_entries, FLAC__bool raw, FILE *f) @@ -255,7 +247,7 @@ void write_vc_fields(const char *filename, const char *field_name, const FLAC__S const unsigned field_name_length = (0 != field_name)? strlen(field_name) : 0; for(i = 0; i < num_entries; i++) { - if(0 == field_name || FLAC__metadata_object_vorbiscomment_entry_matches(entry + i, field_name, field_name_length)) + if(0 == field_name || FLAC__metadata_object_vorbiscomment_entry_matches(entry[i], field_name, field_name_length)) write_vc_field(filename, entry + i, raw, f); } } diff --git a/src/share/grabbag/replaygain.c b/src/share/grabbag/replaygain.c index 98b1088..5417fac 100644 --- a/src/share/grabbag/replaygain.c +++ b/src/share/grabbag/replaygain.c @@ -102,7 +102,7 @@ static FLAC__bool append_tag_(FLAC__StreamMetadata *block, const char *format, c entry.entry = (FLAC__byte *)buffer; entry.length = strlen(buffer); - return FLAC__metadata_object_vorbiscomment_insert_comment(block, block->data.vorbis_comment.num_comments, entry, /*copy=*/true); + return FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/true); } FLAC__bool grabbag__replaygain_is_valid_sample_frequency(unsigned sample_frequency) diff --git a/src/test_libFLAC++/metadata_manip.cpp b/src/test_libFLAC++/metadata_manip.cpp index 1ede420..aee13f7 100644 --- a/src/test_libFLAC++/metadata_manip.cpp +++ b/src/test_libFLAC++/metadata_manip.cpp @@ -500,8 +500,8 @@ static bool generate_file_() vorbiscomment.type = FLAC__METADATA_TYPE_VORBIS_COMMENT; vorbiscomment.length = (4 + vendor_string_length) + 4; vorbiscomment.data.vorbis_comment.vendor_string.length = vendor_string_length; - vorbiscomment.data.vorbis_comment.vendor_string.entry = (FLAC__byte*)malloc_or_die_(vendor_string_length); - memcpy(vorbiscomment.data.vorbis_comment.vendor_string.entry, FLAC__VENDOR_STRING, vendor_string_length); + vorbiscomment.data.vorbis_comment.vendor_string.entry = (FLAC__byte*)malloc_or_die_(vendor_string_length+1); + memcpy(vorbiscomment.data.vorbis_comment.vendor_string.entry, FLAC__VENDOR_STRING, vendor_string_length+1); vorbiscomment.data.vorbis_comment.num_comments = 0; vorbiscomment.data.vorbis_comment.comments = 0; } diff --git a/src/test_libFLAC++/metadata_object.cpp b/src/test_libFLAC++/metadata_object.cpp index 26b5ced..97efbb6 100644 --- a/src/test_libFLAC++/metadata_object.cpp +++ b/src/test_libFLAC++/metadata_object.cpp @@ -120,16 +120,16 @@ static void init_metadata_blocks_() vorbiscomment_.type = ::FLAC__METADATA_TYPE_VORBIS_COMMENT; vorbiscomment_.length = (4 + 5) + 4 + (4 + 12) + (4 + 12); vorbiscomment_.data.vorbis_comment.vendor_string.length = 5; - vorbiscomment_.data.vorbis_comment.vendor_string.entry = (FLAC__byte*)malloc_or_die_(5); - memcpy(vorbiscomment_.data.vorbis_comment.vendor_string.entry, "name0", 5); + vorbiscomment_.data.vorbis_comment.vendor_string.entry = (FLAC__byte*)malloc_or_die_(5+1); + memcpy(vorbiscomment_.data.vorbis_comment.vendor_string.entry, "name0", 5+1); vorbiscomment_.data.vorbis_comment.num_comments = 2; vorbiscomment_.data.vorbis_comment.comments = (::FLAC__StreamMetadata_VorbisComment_Entry*)malloc_or_die_(vorbiscomment_.data.vorbis_comment.num_comments * sizeof(::FLAC__StreamMetadata_VorbisComment_Entry)); vorbiscomment_.data.vorbis_comment.comments[0].length = 12; - vorbiscomment_.data.vorbis_comment.comments[0].entry = (FLAC__byte*)malloc_or_die_(12); - memcpy(vorbiscomment_.data.vorbis_comment.comments[0].entry, "name2=value2", 12); + vorbiscomment_.data.vorbis_comment.comments[0].entry = (FLAC__byte*)malloc_or_die_(12+1); + memcpy(vorbiscomment_.data.vorbis_comment.comments[0].entry, "name2=value2", 12+1); vorbiscomment_.data.vorbis_comment.comments[1].length = 12; - vorbiscomment_.data.vorbis_comment.comments[1].entry = (FLAC__byte*)malloc_or_die_(12); - memcpy(vorbiscomment_.data.vorbis_comment.comments[1].entry, "name3=value3", 12); + vorbiscomment_.data.vorbis_comment.comments[1].entry = (FLAC__byte*)malloc_or_die_(12+1); + memcpy(vorbiscomment_.data.vorbis_comment.comments[1].entry, "name3=value3", 12+1); cuesheet_.is_last = true; cuesheet_.type = ::FLAC__METADATA_TYPE_CUESHEET; @@ -795,12 +795,32 @@ bool test_metadata_object_vorbiscomment() return die_("!is_valid()"); printf("OK\n"); + { + printf("testing Entry::Entry(const char *field)... "); + FLAC::Metadata::VorbisComment::Entry entry2z("name2=value2"); + if(!entry2z.is_valid()) + return die_("!is_valid()"); + if(strcmp(entry2.get_field(), entry2z.get_field())) + return die_("bad value"); + printf("OK\n"); + } + printf("testing Entry::Entry(const char *field_name, const char *field_value, unsigned field_value_length)... "); FLAC::Metadata::VorbisComment::Entry entry3("name3", "value3", strlen("value3")); if(!entry3.is_valid()) return die_("!is_valid()"); printf("OK\n"); + { + printf("testing Entry::Entry(const char *field_name, const char *field_value)... "); + FLAC::Metadata::VorbisComment::Entry entry3z("name3", "value3"); + if(!entry3z.is_valid()) + return die_("!is_valid()"); + if(strcmp(entry3.get_field(), entry3z.get_field())) + return die_("bad value"); + printf("OK\n"); + } + printf("testing Entry::Entry(const Entry &entry)... "); { FLAC::Metadata::VorbisComment::Entry entry2copy(entry2); @@ -867,7 +887,7 @@ bool test_metadata_object_vorbiscomment() return die_("entry mismatch"); printf("OK\n"); - printf("testing Entry::set_field_value()... "); + printf("testing Entry::set_field_value(const char *field_value, unsigned field_value_length)... "); if(!entry1.set_field_value("value1", strlen("value1"))) return die_("returned false"); if(0 != memcmp(entry1.get_field_value(), "value1", strlen("value1"))) @@ -876,7 +896,16 @@ bool test_metadata_object_vorbiscomment() return die_("entry mismatch"); printf("OK\n"); - printf("testing Entry::set_field()... "); + printf("testing Entry::set_field_value(const char *field_value)... "); + if(!entry1.set_field_value("value1")) + return die_("returned false"); + if(0 != memcmp(entry1.get_field_value(), "value1", strlen("value1"))) + return die_("value mismatch"); + if(0 != memcmp(entry1.get_field(), "name1=value1", strlen("name1=value1"))) + return die_("entry mismatch"); + printf("OK\n"); + + printf("testing Entry::set_field(const char *field, unsigned field_length)... "); if(!entry1.set_field("name0=value0", strlen("name0=value0"))) return die_("returned false"); if(0 != memcmp(entry1.get_field_name(), "name0", strlen("name0"))) @@ -887,6 +916,17 @@ bool test_metadata_object_vorbiscomment() return die_("entry mismatch"); printf("OK\n"); + printf("testing Entry::set_field(const char *field)... "); + if(!entry1.set_field("name0=value0")) + return die_("returned false"); + if(0 != memcmp(entry1.get_field_name(), "name0", strlen("name0"))) + return die_("value mismatch"); + if(0 != memcmp(entry1.get_field_value(), "value0", strlen("value0"))) + return die_("value mismatch"); + if(0 != memcmp(entry1.get_field(), "name0=value0", strlen("name0=value0"))) + return die_("entry mismatch"); + printf("OK\n"); + printf("PASSED\n\n"); @@ -989,6 +1029,44 @@ bool test_metadata_object_vorbiscomment() return die_("value mismatch"); printf("OK\n"); + printf("testing VorbisComment::append_comment()... +\n"); + printf(" VorbisComment::get_comment()... "); + if(!block.append_comment(entry3)) + return die_("returned false"); + if(block.get_comment(0).get_field_length() != vorbiscomment_.data.vorbis_comment.comments[1].length) + return die_("length mismatch"); + if(0 != memcmp(block.get_comment(0).get_field(), vorbiscomment_.data.vorbis_comment.comments[1].entry, vorbiscomment_.data.vorbis_comment.comments[1].length)) + return die_("value mismatch"); + printf("OK\n"); + + printf("testing VorbisComment::append_comment()... +\n"); + printf(" VorbisComment::get_comment()... "); + if(!block.append_comment(entry2)) + return die_("returned false"); + if(block.get_comment(1).get_field_length() != vorbiscomment_.data.vorbis_comment.comments[0].length) + return die_("length mismatch"); + if(0 != memcmp(block.get_comment(1).get_field(), vorbiscomment_.data.vorbis_comment.comments[0].entry, vorbiscomment_.data.vorbis_comment.comments[0].length)) + return die_("value mismatch"); + printf("OK\n"); + + printf("testing VorbisComment::delete_comment()... +\n"); + printf(" VorbisComment::get_comment()... "); + if(!block.delete_comment(0)) + return die_("returned false"); + if(block.get_comment(0).get_field_length() != vorbiscomment_.data.vorbis_comment.comments[0].length) + return die_("length[0] mismatch"); + if(0 != memcmp(block.get_comment(0).get_field(), vorbiscomment_.data.vorbis_comment.comments[0].entry, vorbiscomment_.data.vorbis_comment.comments[0].length)) + return die_("value[0] mismatch"); + printf("OK\n"); + + printf("testing VorbisComment::delete_comment()... +\n"); + printf(" VorbisComment::get_comment()... "); + if(!block.delete_comment(0)) + return die_("returned false"); + if(block.get_num_comments() != 0) + return die_("block mismatch, expected num_comments = 0"); + printf("OK\n"); + printf("testing VorbisComment::insert_comment()... +\n"); printf(" VorbisComment::get_comment()... "); if(!block.insert_comment(0, entry3)) diff --git a/src/test_libFLAC++/metadata_utils.c b/src/test_libFLAC++/metadata_utils.c index 747ab06..7dc57ac 100644 --- a/src/test_libFLAC++/metadata_utils.c +++ b/src/test_libFLAC++/metadata_utils.c @@ -429,13 +429,13 @@ void mutils__init_metadata_blocks( vorbiscomment->type = FLAC__METADATA_TYPE_VORBIS_COMMENT; vorbiscomment->length = (4 + vendor_string_length) + 4 + (4 + 5) + (4 + 0); vorbiscomment->data.vorbis_comment.vendor_string.length = vendor_string_length; - vorbiscomment->data.vorbis_comment.vendor_string.entry = (FLAC__byte*)malloc_or_die_(vendor_string_length); - memcpy(vorbiscomment->data.vorbis_comment.vendor_string.entry, FLAC__VENDOR_STRING, vendor_string_length); + vorbiscomment->data.vorbis_comment.vendor_string.entry = (FLAC__byte*)malloc_or_die_(vendor_string_length+1); + memcpy(vorbiscomment->data.vorbis_comment.vendor_string.entry, FLAC__VENDOR_STRING, vendor_string_length+1); vorbiscomment->data.vorbis_comment.num_comments = 2; vorbiscomment->data.vorbis_comment.comments = (FLAC__StreamMetadata_VorbisComment_Entry*)malloc_or_die_(vorbiscomment->data.vorbis_comment.num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry)); vorbiscomment->data.vorbis_comment.comments[0].length = 5; - vorbiscomment->data.vorbis_comment.comments[0].entry = (FLAC__byte*)malloc_or_die_(5); - memcpy(vorbiscomment->data.vorbis_comment.comments[0].entry, "ab=cd", 5); + vorbiscomment->data.vorbis_comment.comments[0].entry = (FLAC__byte*)malloc_or_die_(5+1); + memcpy(vorbiscomment->data.vorbis_comment.comments[0].entry, "ab=cd", 5+1); vorbiscomment->data.vorbis_comment.comments[1].length = 0; vorbiscomment->data.vorbis_comment.comments[1].entry = 0; } diff --git a/src/test_libFLAC/metadata_manip.c b/src/test_libFLAC/metadata_manip.c index b0c2a27..5cd4c08 100644 --- a/src/test_libFLAC/metadata_manip.c +++ b/src/test_libFLAC/metadata_manip.c @@ -507,8 +507,8 @@ static FLAC__bool generate_file_() vorbiscomment.type = FLAC__METADATA_TYPE_VORBIS_COMMENT; vorbiscomment.length = (4 + vendor_string_length) + 4; vorbiscomment.data.vorbis_comment.vendor_string.length = vendor_string_length; - vorbiscomment.data.vorbis_comment.vendor_string.entry = malloc_or_die_(vendor_string_length); - memcpy(vorbiscomment.data.vorbis_comment.vendor_string.entry, FLAC__VENDOR_STRING, vendor_string_length); + vorbiscomment.data.vorbis_comment.vendor_string.entry = malloc_or_die_(vendor_string_length+1); + memcpy(vorbiscomment.data.vorbis_comment.vendor_string.entry, FLAC__VENDOR_STRING, vendor_string_length+1); vorbiscomment.data.vorbis_comment.num_comments = 0; vorbiscomment.data.vorbis_comment.comments = 0; } diff --git a/src/test_libFLAC/metadata_object.c b/src/test_libFLAC/metadata_object.c index cd8a527..aee8de1 100644 --- a/src/test_libFLAC/metadata_object.c +++ b/src/test_libFLAC/metadata_object.c @@ -142,16 +142,18 @@ static FLAC__bool check_seektable_(const FLAC__StreamMetadata *block, unsigned n static void entry_new_(FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *field) { entry->length = strlen(field); - entry->entry = (FLAC__byte*)malloc(entry->length); + entry->entry = (FLAC__byte*)malloc(entry->length+1); FLAC__ASSERT(0 != entry->entry); memcpy(entry->entry, field, entry->length); + entry->entry[entry->length] = '\0'; } static void entry_clone_(FLAC__StreamMetadata_VorbisComment_Entry *entry) { - FLAC__byte *x = (FLAC__byte*)malloc(entry->length); + FLAC__byte *x = (FLAC__byte*)malloc(entry->length+1); FLAC__ASSERT(0 != x); memcpy(x, entry->entry, entry->length); + x[entry->length] = '\0'; entry->entry = x; } @@ -200,6 +202,18 @@ static void vc_resize_(FLAC__StreamMetadata *block, unsigned num) vc_calc_len_(block); } +static int vc_find_from_(FLAC__StreamMetadata *block, const char *name, unsigned start) +{ + const unsigned n = strlen(name); + unsigned i; + for(i = start; i < block->data.vorbis_comment.num_comments; i++) { + const FLAC__StreamMetadata_VorbisComment_Entry *entry = &block->data.vorbis_comment.comments[i]; + if(entry->length > n && 0 == strncmp(entry->entry, name, n) && entry->entry[n] == '=') + return (int)i; + } + return -1; +} + static void vc_set_vs_new_(FLAC__StreamMetadata_VorbisComment_Entry *entry, FLAC__StreamMetadata *block, const char *field) { if(0 != block->data.vorbis_comment.vendor_string.entry) @@ -238,6 +252,30 @@ static void vc_delete_(FLAC__StreamMetadata *block, unsigned pos) vc_calc_len_(block); } +static void vc_replace_new_(FLAC__StreamMetadata_VorbisComment_Entry *entry, FLAC__StreamMetadata *block, const char *field, FLAC__bool all) +{ + int index; + char field_name[256]; + const char *eq = strchr(field, '='); + FLAC__ASSERT(eq>field && (unsigned)(eq-field) < sizeof(field_name)); + memcpy(field_name, field, eq-field); + field_name[eq-field]='\0'; + + index = vc_find_from_(block, field_name, 0); + if(index < 0) + vc_insert_new_(entry, block, block->data.vorbis_comment.num_comments, field); + else { + vc_set_new_(entry, block, (unsigned)index, field); + if(all) { + for(index = index+1; index >= 0 && (unsigned)index < block->data.vorbis_comment.num_comments; ) + if((index = vc_find_from_(block, field_name, (unsigned)index)) >= 0) + vc_delete_(block, (unsigned)index); + } + } + + vc_calc_len_(block); +} + static void track_new_(FLAC__StreamMetadata_CueSheet_Track *track, FLAC__uint64 offset, FLAC__byte number, const char *isrc, FLAC__bool data, FLAC__bool pre_em) { track->offset = offset; @@ -430,6 +468,7 @@ FLAC__bool test_metadata_object() FLAC__StreamMetadata_CueSheet_Index index; FLAC__StreamMetadata_CueSheet_Track track; unsigned i, expected_length, seekpoints; + int j; static FLAC__byte dummydata[4] = { 'a', 'b', 'c', 'd' }; printf("\n+++ libFLAC unit test: metadata objects\n\n"); @@ -843,6 +882,55 @@ FLAC__bool test_metadata_object() printf("testing VORBIS_COMMENT\n"); + { + FLAC__StreamMetadata_VorbisComment_Entry entry_; + char *field_name, *field_value; + + printf("testing FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair()... "); + if(!FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry_, "name", "value")) { + printf("FAILED, returned false\n"); + return false; + } + if(strcmp(entry_.entry, "name=value")) { + printf("FAILED, field mismatch\n"); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair()... "); + if(!FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(entry_, &field_name, &field_value)) { + printf("FAILED, returned false\n"); + return false; + } + if(strcmp(field_name, "name")) { + printf("FAILED, field name mismatch\n"); + return false; + } + if(strcmp(field_value, "value")) { + printf("FAILED, field value mismatch\n"); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_entry_matches()... "); + if(!FLAC__metadata_object_vorbiscomment_entry_matches(entry_, field_name, strlen(field_name))) { + printf("FAILED, expected true, returned false\n"); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_entry_matches()... "); + if(FLAC__metadata_object_vorbiscomment_entry_matches(entry_, "blah", strlen("blah"))) { + printf("FAILED, expected false, returned true\n"); + return false; + } + printf("OK\n"); + + free(entry_.entry); + free(field_name); + free(field_value); + } + printf("testing FLAC__metadata_object_new()... "); block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT); if(0 == block) { @@ -896,6 +984,36 @@ FLAC__bool test_metadata_object() return false; printf("OK\n"); + printf("testing FLAC__metadata_object_vorbiscomment_append_comment(copy) on empty array..."); + vc_insert_new_(&entry, vorbiscomment, 0, "name1=field1"); + if(!FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_append_comment(copy) on non-empty array..."); + vc_insert_new_(&entry, vorbiscomment, 1, "name2=field2"); + if(!FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + vc_resize_(vorbiscomment, 0); + printf("testing FLAC__metadata_object_vorbiscomment_resize_comments(shrink to %u)...", vorbiscomment->data.vorbis_comment.num_comments); + if(!FLAC__metadata_object_vorbiscomment_resize_comments(block, vorbiscomment->data.vorbis_comment.num_comments)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + printf("testing FLAC__metadata_object_vorbiscomment_insert_comment(copy) on empty array..."); vc_insert_new_(&entry, vorbiscomment, 0, "name1=field1"); if(!FLAC__metadata_object_vorbiscomment_insert_comment(block, 0, entry, /*copy=*/true)) { @@ -936,6 +1054,96 @@ FLAC__bool test_metadata_object() return false; printf("OK\n"); + printf("testing FLAC__metadata_object_vorbiscomment_insert_comment(copy) on end of non-empty array..."); + vc_insert_new_(&entry, vorbiscomment, 4, "name3=field3dup1"); + if(!FLAC__metadata_object_vorbiscomment_insert_comment(block, 4, entry, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_insert_comment(copy) on end of non-empty array..."); + vc_insert_new_(&entry, vorbiscomment, 5, "name3=field3dup1"); + if(!FLAC__metadata_object_vorbiscomment_insert_comment(block, 5, entry, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_find_entry_from()..."); + if((j = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, "name3")) != 1) { + printf("FAILED, expected 1, got %d\n", j); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_find_entry_from()..."); + if((j = FLAC__metadata_object_vorbiscomment_find_entry_from(block, j+1, "name3")) != 4) { + printf("FAILED, expected 4, got %d\n", j); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_find_entry_from()..."); + if((j = FLAC__metadata_object_vorbiscomment_find_entry_from(block, j+1, "name3")) != 5) { + printf("FAILED, expected 5, got %d\n", j); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_find_entry_from()..."); + if((j = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, "name2")) != 0) { + printf("FAILED, expected 0, got %d\n", j); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_find_entry_from()..."); + if((j = FLAC__metadata_object_vorbiscomment_find_entry_from(block, j+1, "name2")) != -1) { + printf("FAILED, expected -1, got %d\n", j); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_find_entry_from()..."); + if((j = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, "blah")) != -1) { + printf("FAILED, expected -1, got %d\n", j); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_replace_comment(first, copy)..."); + vc_replace_new_(&entry, vorbiscomment, "name3=field3new1", /*all=*/false); + if(!FLAC__metadata_object_vorbiscomment_replace_comment(block, entry, /*all=*/false, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + if(block->data.vorbis_comment.num_comments != 6) { + printf("FAILED, expected 6 comments, got %u\n", block->data.vorbis_comment.num_comments); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_replace_comment(all, copy)..."); + vc_replace_new_(&entry, vorbiscomment, "name3=field3new2", /*all=*/true); + if(!FLAC__metadata_object_vorbiscomment_replace_comment(block, entry, /*all=*/true, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + if(block->data.vorbis_comment.num_comments != 4) { + printf("FAILED, expected 4 comments, got %u\n", block->data.vorbis_comment.num_comments); + return false; + } + printf("OK\n"); + printf("testing FLAC__metadata_object_clone()... "); blockcopy = FLAC__metadata_object_clone(block); if(0 == blockcopy) { @@ -980,6 +1188,91 @@ FLAC__bool test_metadata_object() return false; printf("OK\n"); + printf("testing FLAC__metadata_object_vorbiscomment_append_comment(copy) on non-empty array..."); + vc_insert_new_(&entry, vorbiscomment, 1, "rem0=val0"); + if(!FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_append_comment(copy) on non-empty array..."); + vc_insert_new_(&entry, vorbiscomment, 2, "rem0=val1"); + if(!FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_append_comment(copy) on non-empty array..."); + vc_insert_new_(&entry, vorbiscomment, 3, "rem0=val2"); + if(!FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_remove_entry_matching(\"blah\")..."); + if((j = FLAC__metadata_object_vorbiscomment_remove_entry_matching(block, "blah")) != 0) { + printf("FAILED, expected 0, got %d\n", j); + return false; + } + if(block->data.vorbis_comment.num_comments != 4) { + printf("FAILED, expected 4 comments, got %u\n", block->data.vorbis_comment.num_comments); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_remove_entry_matching(\"rem0\")..."); + vc_delete_(vorbiscomment, 1); + if((j = FLAC__metadata_object_vorbiscomment_remove_entry_matching(block, "rem0")) != 1) { + printf("FAILED, expected 1, got %d\n", j); + return false; + } + if(block->data.vorbis_comment.num_comments != 3) { + printf("FAILED, expected 3 comments, got %u\n", block->data.vorbis_comment.num_comments); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_remove_entries_matching(\"blah\")..."); + if((j = FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, "blah")) != 0) { + printf("FAILED, expected 0, got %d\n", j); + return false; + } + if(block->data.vorbis_comment.num_comments != 3) { + printf("FAILED, expected 3 comments, got %u\n", block->data.vorbis_comment.num_comments); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_remove_entries_matching(\"rem0\")..."); + vc_delete_(vorbiscomment, 1); + vc_delete_(vorbiscomment, 1); + if((j = FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, "rem0")) != 2) { + printf("FAILED, expected 2, got %d\n", j); + return false; + } + if(block->data.vorbis_comment.num_comments != 1) { + printf("FAILED, expected 1 comments, got %u\n", block->data.vorbis_comment.num_comments); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + printf("testing FLAC__metadata_object_vorbiscomment_set_comment(copy)..."); vc_set_new_(&entry, vorbiscomment, 0, "name5=field5"); FLAC__metadata_object_vorbiscomment_set_comment(block, 0, entry, /*copy=*/true); @@ -1018,6 +1311,51 @@ FLAC__bool test_metadata_object() return false; printf("OK\n"); + printf("testing FLAC__metadata_object_vorbiscomment_append_comment(own) on empty array..."); + vc_insert_new_(&entry, vorbiscomment, 0, "name1=field1"); + entry_clone_(&entry); + if(!FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_append_comment(own) on non-empty array..."); + vc_insert_new_(&entry, vorbiscomment, 1, "name2=field2"); + entry_clone_(&entry); + if(!FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_delete()... "); + FLAC__metadata_object_delete(vorbiscomment); + FLAC__metadata_object_delete(block); + printf("OK\n"); + + printf("testing FLAC__metadata_object_new()... "); + block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT); + if(0 == block) { + printf("FAILED, returned NULL\n"); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_clone()... "); + vorbiscomment = FLAC__metadata_object_clone(block); + if(0 == vorbiscomment) { + printf("FAILED, returned NULL\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + printf("testing FLAC__metadata_object_vorbiscomment_insert_comment(own) on empty array..."); vc_insert_new_(&entry, vorbiscomment, 0, "name1=field1"); entry_clone_(&entry); @@ -1062,6 +1400,58 @@ FLAC__bool test_metadata_object() return false; printf("OK\n"); + printf("testing FLAC__metadata_object_vorbiscomment_insert_comment(own) on end of non-empty array..."); + vc_insert_new_(&entry, vorbiscomment, 4, "name3=field3dup1"); + entry_clone_(&entry); + if(!FLAC__metadata_object_vorbiscomment_insert_comment(block, 4, entry, /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_insert_comment(own) on end of non-empty array..."); + vc_insert_new_(&entry, vorbiscomment, 5, "name3=field3dup1"); + entry_clone_(&entry); + if(!FLAC__metadata_object_vorbiscomment_insert_comment(block, 5, entry, /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_replace_comment(first, own)..."); + vc_replace_new_(&entry, vorbiscomment, "name3=field3new1", /*all=*/false); + entry_clone_(&entry); + if(!FLAC__metadata_object_vorbiscomment_replace_comment(block, entry, /*all=*/false, /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + if(block->data.vorbis_comment.num_comments != 6) { + printf("FAILED, expected 6 comments, got %u\n", block->data.vorbis_comment.num_comments); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_replace_comment(all, own)..."); + vc_replace_new_(&entry, vorbiscomment, "name3=field3new2", /*all=*/true); + entry_clone_(&entry); + if(!FLAC__metadata_object_vorbiscomment_replace_comment(block, entry, /*all=*/true, /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + if(block->data.vorbis_comment.num_comments != 4) { + printf("FAILED, expected 4 comments, got %u\n", block->data.vorbis_comment.num_comments); + return false; + } + printf("OK\n"); + printf("testing FLAC__metadata_object_vorbiscomment_delete_comment() on middle of array..."); vc_delete_(vorbiscomment, 2); if(!FLAC__metadata_object_vorbiscomment_delete_comment(block, 2)) { @@ -1114,7 +1504,6 @@ FLAC__bool test_metadata_object() printf("OK\n"); - printf("testing CUESHEET\n"); { diff --git a/src/test_libFLAC/metadata_utils.c b/src/test_libFLAC/metadata_utils.c index 747ab06..7dc57ac 100644 --- a/src/test_libFLAC/metadata_utils.c +++ b/src/test_libFLAC/metadata_utils.c @@ -429,13 +429,13 @@ void mutils__init_metadata_blocks( vorbiscomment->type = FLAC__METADATA_TYPE_VORBIS_COMMENT; vorbiscomment->length = (4 + vendor_string_length) + 4 + (4 + 5) + (4 + 0); vorbiscomment->data.vorbis_comment.vendor_string.length = vendor_string_length; - vorbiscomment->data.vorbis_comment.vendor_string.entry = (FLAC__byte*)malloc_or_die_(vendor_string_length); - memcpy(vorbiscomment->data.vorbis_comment.vendor_string.entry, FLAC__VENDOR_STRING, vendor_string_length); + vorbiscomment->data.vorbis_comment.vendor_string.entry = (FLAC__byte*)malloc_or_die_(vendor_string_length+1); + memcpy(vorbiscomment->data.vorbis_comment.vendor_string.entry, FLAC__VENDOR_STRING, vendor_string_length+1); vorbiscomment->data.vorbis_comment.num_comments = 2; vorbiscomment->data.vorbis_comment.comments = (FLAC__StreamMetadata_VorbisComment_Entry*)malloc_or_die_(vorbiscomment->data.vorbis_comment.num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry)); vorbiscomment->data.vorbis_comment.comments[0].length = 5; - vorbiscomment->data.vorbis_comment.comments[0].entry = (FLAC__byte*)malloc_or_die_(5); - memcpy(vorbiscomment->data.vorbis_comment.comments[0].entry, "ab=cd", 5); + vorbiscomment->data.vorbis_comment.comments[0].entry = (FLAC__byte*)malloc_or_die_(5+1); + memcpy(vorbiscomment->data.vorbis_comment.comments[0].entry, "ab=cd", 5+1); vorbiscomment->data.vorbis_comment.comments[1].length = 0; vorbiscomment->data.vorbis_comment.comments[1].entry = 0; } diff --git a/src/test_libOggFLAC++/metadata_utils.c b/src/test_libOggFLAC++/metadata_utils.c index 747ab06..7dc57ac 100644 --- a/src/test_libOggFLAC++/metadata_utils.c +++ b/src/test_libOggFLAC++/metadata_utils.c @@ -429,13 +429,13 @@ void mutils__init_metadata_blocks( vorbiscomment->type = FLAC__METADATA_TYPE_VORBIS_COMMENT; vorbiscomment->length = (4 + vendor_string_length) + 4 + (4 + 5) + (4 + 0); vorbiscomment->data.vorbis_comment.vendor_string.length = vendor_string_length; - vorbiscomment->data.vorbis_comment.vendor_string.entry = (FLAC__byte*)malloc_or_die_(vendor_string_length); - memcpy(vorbiscomment->data.vorbis_comment.vendor_string.entry, FLAC__VENDOR_STRING, vendor_string_length); + vorbiscomment->data.vorbis_comment.vendor_string.entry = (FLAC__byte*)malloc_or_die_(vendor_string_length+1); + memcpy(vorbiscomment->data.vorbis_comment.vendor_string.entry, FLAC__VENDOR_STRING, vendor_string_length+1); vorbiscomment->data.vorbis_comment.num_comments = 2; vorbiscomment->data.vorbis_comment.comments = (FLAC__StreamMetadata_VorbisComment_Entry*)malloc_or_die_(vorbiscomment->data.vorbis_comment.num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry)); vorbiscomment->data.vorbis_comment.comments[0].length = 5; - vorbiscomment->data.vorbis_comment.comments[0].entry = (FLAC__byte*)malloc_or_die_(5); - memcpy(vorbiscomment->data.vorbis_comment.comments[0].entry, "ab=cd", 5); + vorbiscomment->data.vorbis_comment.comments[0].entry = (FLAC__byte*)malloc_or_die_(5+1); + memcpy(vorbiscomment->data.vorbis_comment.comments[0].entry, "ab=cd", 5+1); vorbiscomment->data.vorbis_comment.comments[1].length = 0; vorbiscomment->data.vorbis_comment.comments[1].entry = 0; } diff --git a/src/test_libOggFLAC/metadata_utils.c b/src/test_libOggFLAC/metadata_utils.c index 747ab06..7dc57ac 100644 --- a/src/test_libOggFLAC/metadata_utils.c +++ b/src/test_libOggFLAC/metadata_utils.c @@ -429,13 +429,13 @@ void mutils__init_metadata_blocks( vorbiscomment->type = FLAC__METADATA_TYPE_VORBIS_COMMENT; vorbiscomment->length = (4 + vendor_string_length) + 4 + (4 + 5) + (4 + 0); vorbiscomment->data.vorbis_comment.vendor_string.length = vendor_string_length; - vorbiscomment->data.vorbis_comment.vendor_string.entry = (FLAC__byte*)malloc_or_die_(vendor_string_length); - memcpy(vorbiscomment->data.vorbis_comment.vendor_string.entry, FLAC__VENDOR_STRING, vendor_string_length); + vorbiscomment->data.vorbis_comment.vendor_string.entry = (FLAC__byte*)malloc_or_die_(vendor_string_length+1); + memcpy(vorbiscomment->data.vorbis_comment.vendor_string.entry, FLAC__VENDOR_STRING, vendor_string_length+1); vorbiscomment->data.vorbis_comment.num_comments = 2; vorbiscomment->data.vorbis_comment.comments = (FLAC__StreamMetadata_VorbisComment_Entry*)malloc_or_die_(vorbiscomment->data.vorbis_comment.num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry)); vorbiscomment->data.vorbis_comment.comments[0].length = 5; - vorbiscomment->data.vorbis_comment.comments[0].entry = (FLAC__byte*)malloc_or_die_(5); - memcpy(vorbiscomment->data.vorbis_comment.comments[0].entry, "ab=cd", 5); + vorbiscomment->data.vorbis_comment.comments[0].entry = (FLAC__byte*)malloc_or_die_(5+1); + memcpy(vorbiscomment->data.vorbis_comment.comments[0].entry, "ab=cd", 5+1); vorbiscomment->data.vorbis_comment.comments[1].length = 0; vorbiscomment->data.vorbis_comment.comments[1].entry = 0; } -- 2.7.4