additions to metadata object api: more vorbiscomment functions, trailing-null on...
authorJosh Coalson <jcoalson@users.sourceforce.net>
Thu, 30 Dec 2004 00:59:30 +0000 (00:59 +0000)
committerJosh Coalson <jcoalson@users.sourceforce.net>
Thu, 30 Dec 2004 00:59:30 +0000 (00:59 +0000)
20 files changed:
doc/html/changelog.html
include/FLAC++/metadata.h
include/FLAC/format.h
include/FLAC/metadata.h
src/flac/vorbiscomment.c
src/libFLAC++/metadata.cpp
src/libFLAC/metadata_iterators.c
src/libFLAC/metadata_object.c
src/libFLAC/stream_decoder.c
src/metaflac/operations_shorthand_vorbiscomment.c
src/metaflac/utils.c
src/share/grabbag/replaygain.c
src/test_libFLAC++/metadata_manip.cpp
src/test_libFLAC++/metadata_object.cpp
src/test_libFLAC++/metadata_utils.c
src/test_libFLAC/metadata_manip.c
src/test_libFLAC/metadata_object.c
src/test_libFLAC/metadata_utils.c
src/test_libOggFLAC++/metadata_utils.c
src/test_libOggFLAC/metadata_utils.c

index 3b5cf76..77168cb 100644 (file)
        </P>
 
        <P>
+               <A NAME="flac_@@@@@@"><B>@@@@@@</B></A>
+       </P>
+       <P>
+       <UL>
+               <LI>
+                       General:
+                       <UL>
+                       </UL>
+               </LI>
+               <LI>
+                       FLAC format:
+                       <UL>
+                       </UL>
+               </LI>
+               <LI>
+                       Ogg FLAC format:
+                       <UL>
+                       </UL>
+               </LI>
+               <LI>
+                       flac:
+                       <UL>
+                               <LI>New option <A HREF="documentation.html#flac_options_input_size"><TT>--input-size</TT></A> to manually specify the input size when encoding raw samples from stdin.</LI>
+                       </UL>
+               </LI>
+               <LI>
+                       metaflac:
+                       <UL>
+                       </UL>
+               </LI>
+               <LI>
+                       plugins:
+                       <UL>
+                               <LI>Added support for HTTP streaming in XMMS plugin.</LI>
+                       </UL>
+               </LI>
+               <LI>
+                       build system:
+                       <UL>
+                       </UL>
+               </LI>
+               <LI>
+                       libraries:
+                       <UL>
+                               <LI>libFLAC, libOggFLAC: Can now be compiled to use only integer instructions, including encoding.  The decoder is almost completely integer anyway but there were a couple places that needed a fixed-point replacement.  There is no fixed-point version of LPC analysis yet, so if libFLAC is compiled integer-only, it will behave as if the max LPC order is 0 (i.e. used fixed predictors only).</LI>
+                       </UL>
+               </LI>
+               <LI>
+                       Interface changes:
+                       <UL>
+                               <LI>
+                                       libFLAC:
+                                       <UL>
+                                               <LI>Metadata interface now maintains a trailing NULL on Vorbis comment entries for convenience.</LI>
+                                               <LI><B>Added</B> FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair()</LI>
+                                               <LI><B>Added</B> FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair()</LI>
+                                               <LI><B>Changed</B> the signature of FLAC__metadata_object_vorbiscomment_entry_matches(): the first argument is now <TT>FLAC__StreamMetadata_VorbisComment_Entry entry</TT> (was <TT>const FLAC__StreamMetadata_VorbisComment_Entry *entry</TT>), i.e. <TT>entry</TT> is now pass-by-value.</LI>
+                                       </UL>
+                               </LI>
+                               <LI>
+                                       libFLAC++:
+                                       <UL>
+                                               <LI>Metadata interface now maintains a trailing NULL on Vorbis comment values for convenience.</LI>
+                                               <LI><B>Added</B> methods to FLAC::Metadata::VorbisComment::Entry for setting comment values from null-terminated strings:
+                                                       <UL>
+                                                               <LI>Entry(const char *field)</LI>
+                                                               <LI>Entry(const char *field_name, const char *field_value)</LI>
+                                                               <LI>bool set_field(const char *field)</LI>
+                                                               <LI>bool set_field_value(const char *field_value)</LI>
+                                                       </UL>
+                                               </LI>
+                                       </UL>
+                               </LI>
+                               <LI>
+                                       libOggFLAC:
+                                       <UL>
+                                       </UL>
+                               </LI>
+                               <LI>
+                                       libOggFLAC++:
+                                       <UL>
+                                       </UL>
+                               </LI>
+                       </UL>
+               </LI>
+       </UL>
+       </P>
+
+       <P>
                <A NAME="flac_1_1_1"><B>FLAC 1.1.1</B></A>
        </P>
        <P>
index d7e91a6..138580e 100644 (file)
@@ -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);
                };
index e003c34..d4e6bca 100644 (file)
@@ -583,6 +583,15 @@ typedef struct {
 
 
 /** Vorbis comment entry structure used in VORBIS_COMMENT blocks.  (c.f. <A HREF="../format.html#metadata_block_vorbis_comment">format specification</A>)
+ *
+ *  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;
index bd80821..76893af 100644 (file)
@@ -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);
 
index 15e94e9..2749521 100644 (file)
@@ -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";
index 0a9cd6e..871b01e 100644 (file)
@@ -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());
index 45e5c3f..13f1c3f 100644 (file)
@@ -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;
index 589357b..3a613a1 100644 (file)
@@ -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);
                }
index dc68710..5995afe 100644 (file)
@@ -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;
index 6f14681..f92d879 100644 (file)
@@ -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);
index 9983d9b..745e70f 100644 (file)
@@ -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);
        }
 }
index 98b1088..5417fac 100644 (file)
@@ -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)
index 1ede420..aee13f7 100644 (file)
@@ -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;
        }
index 26b5ced..97efbb6 100644 (file)
@@ -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))
index 747ab06..7dc57ac 100644 (file)
@@ -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;
        }
index b0c2a27..5cd4c08 100644 (file)
@@ -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;
        }
index cd8a527..aee8de1 100644 (file)
@@ -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");
 
        {
index 747ab06..7dc57ac 100644 (file)
@@ -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;
        }
index 747ab06..7dc57ac 100644 (file)
@@ -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;
        }
index 747ab06..7dc57ac 100644 (file)
@@ -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;
        }