Permalink
Browse files

Add ApeTag_iter_items for iterating over all items in tag

Unlike ApeTag_get_items, this does not require allocation of
additional memory.

While here, fix return codes in the man page, and some now
incorrect comments.
  • Loading branch information...
1 parent eb3bb35 commit c17a85809c6d70f0d4339e461cf8746dc5ff7087 @jeremyevans committed Jul 16, 2012
Showing with 120 additions and 32 deletions.
  1. +5 −10 c/apeinfo.c
  2. +27 −11 c/apetag.3
  3. +50 −11 c/apetag.c
  4. +2 −0 c/apetag.h
  5. +36 −0 c/test/test_apetag.c
View
@@ -79,23 +79,18 @@ int ApeInfo_process(char *filename) {
return ret;
}
+int ApeTag_iter_print(struct ApeTag *tag, struct ApeItem *item, void *data) {
+ ApeItem_print(item);
+}
+
/* Prints all items in the tag, one per line. */
void ApeTag_print(struct ApeTag *tag) {
- uint32_t item_count;
- struct ApeItem **items, **is;
-
assert(tag != NULL);
- items = ApeTag_get_items(tag, &item_count);
- if (items == NULL) {
+ if (ApeTag_iter_items(tag, ApeTag_iter_print, NULL) < 0) {
printf("Error getting items: %s", ApeTag_error(tag));
- } else {
- for (is = items; *is; is++) {
- ApeItem_print(*is);
- }
}
- free(items);
printf("\n");
}
View
@@ -33,6 +33,8 @@
.P
.B struct ApeItem ** ApeTag_get_items(struct ApeTag *tag, uint32_t *item_count);
.P
+.B int ApeTag_iter_items(struct ApeTag *tag, int iterator(struct ApeTag *tag, struct ApeItem *item, void *data), void *data);
+.P
.B uint32_t ApeTag_size(struct ApeTag *tag);
.P
.B uint32_t ApeTag_item_count(struct ApeTag *tag);
@@ -205,15 +207,15 @@ Checks if the file associated with
.I tag
already contains a valid APE tag.
.P
-Returns 1 if an APE tag exists, 0 if it does not, <0 on error.
+Returns 1 if an APE tag exists, 0 if it does not, -1 on error.
.P
.B int ApeTag_exists_id3(struct ApeTag *tag);
.P
Checks if the file associated with
.I tag
already contains a valid ID3v1 tag.
.P
-Returns 1 if an ID3v1 tag exists, 0 if it does not, <0 on error.
+Returns 1 if an ID3v1 tag exists, 0 if it does not, -1 on error.
.P
.B int ApeTag_remove(struct ApeTag *tag);
.P
@@ -225,7 +227,7 @@ This function parses the header and footer of the tag and will error instead
of removing a tag if the header or footer of the tag is corrupt.
.P
Returns 1 if the tag doesn't exist, 0 if it does exist and the tag was
-removed successfully, <0 on error.
+removed successfully, -1 on error.
.P
.B int ApeTag_raw(struct ApeTag *tag, char **raw, uint32_t *raw_size);
.P
@@ -240,7 +242,7 @@ The caller is responsible for
freeing
.IR *raw.
.P
-Returns 0 on success, <0 on error.
+Returns 0 on success, -1 on error.
.P
.B int ApeTag_parse(struct ApeTag *tag);
.P
@@ -255,7 +257,7 @@ This is basically the same as calling
.BR ApeTag_add_item
manually with each item already in the tag.
.P
-Returns 0 on success, <0 on error.
+Returns 0 on success, -1 on error.
.P
.B int ApeTag_update(struct ApeTag *tag);
.P
@@ -276,7 +278,7 @@ Writes an ID3v1 tag as well as an APEv2 tag unless the
flag is used or the file already has an APEv2
tag but doesn't have an ID3v1 tag.
.P
-Returns 0 on success, <0 on error.
+Returns 0 on success, -1 on error.
.P
.B int ApeTag_add_item(struct ApeTag *tag, struct ApeItem *item);
.P
@@ -295,7 +297,7 @@ must be created on the heap, as they are all freed when calling
or
.BR ApeTag_remove_item .
.P
-Returns 0 on success, <0 on error.
+Returns 0 on success, -1 on error.
.P
.B int ApeTag_replace_item(struct ApeTag *tag, struct ApeItem *item);
.P
@@ -306,13 +308,13 @@ Otherwise, if the item already exists, remove the existing item
and replace it with the given item.
.P
Returns 0 on success if the item doesn't exist, 1 on success if it already
-existed, <0 on error.
+existed, -1 on error.
.P
.B int ApeTag_remove_item(struct ApeTag *tag, const char *key);
.P
Removes the item with a matching key from the tag.
.P
-Returns 0 on success, 1 if the item did not exist in the tag, <0 on error.
+Returns 0 on success, 1 if the item did not exist in the tag, -1 on error.
.P
.B int ApeTag_clear_items(struct ApeTag *tag);
.P
@@ -346,7 +348,21 @@ The returned array is always terminated by NULL, and always contains at least
It is the caller's responsibility to free the returned array, but the individual
items in the array should not be freed by the caller.
.P
-Returns 0 on success, <0 on error.
+Returns 0 on success, -1 on error.
+.P
+.B int ApeTag_iter_items(struct ApeTag *tag, int iterator(struct ApeTag *tag, struct ApeItem *item, void *data), void *data);
+.P
+Iterates over all of the items in the tag.
+For each item in the tag, calls the iterator function with the tag,
+a pointer to the item, and the data pointer passed to the function.
+The data pointer is not used by the library, but it allows the iterator
+function to communicate back to the calling function.
+.P
+The iterator function should return 0 to continue iteration. Any other value
+will signal the library to stop iterating.
+.P
+Returns 0 if iteration completed successfully, 1 if the iteration was
+terminated early, and -1 if there was an error.
.P
.B uint32_t ApeTag_size(struct ApeTag *tag);
.P
@@ -417,7 +433,7 @@ If this function is called before creating threads, then libapetag
is thread-safe assuming you do not have multiple threads operating
on the same ApeTag or ApeItem struct concurrently.
.P
-Returns 0 on success, <0 on error.
+Returns 0 on success, -1 on error.
.SH AUTHOR
.B apetag
is written by Jeremy Evans. You can contact the author at
View
@@ -135,6 +135,7 @@ static uint32_t ApeTag__tag_length(struct ApeTag *tag);
static uint32_t ApeTag__id3_length(struct ApeTag *tag);
static struct ApeItem * ApeTag__get_item(struct ApeTag *tag, const char *key);
static struct ApeItem **ApeTag__get_items(struct ApeTag *tag, uint32_t *item_count);
+static int ApeTag__iter_items(struct ApeTag *tag, int iterator(struct ApeTag *tag, struct ApeItem *item, void *data), void *data);
static void ApeItem__free(struct ApeItem **item);
static char * ApeTag__strcasecpy(const char *src, size_t size);
@@ -505,6 +506,14 @@ struct ApeItem ** ApeTag_get_items(struct ApeTag *tag, uint32_t *item_count) {
return ApeTag__get_items(tag, item_count);
}
+int ApeTag_iter_items(struct ApeTag *tag, int iterator(struct ApeTag *tag, struct ApeItem *item, void *data), void *data) {
+ if (ApeTag__get_tag_information(tag) != 0) {
+ return -1;
+ }
+
+ return ApeTag__iter_items(tag, iterator, data);
+}
+
int ApeTag_mt_init(void) {
struct ApeTag tag;
@@ -1587,13 +1596,11 @@ static uint32_t ApeTag__id3_length(struct ApeTag *tag) {
}
/*
-Update the passed in **item pointer to point to a struct ApeItem * for the matching
-item in the database.
+Return an ApeItem * corresponding to the passed key, which the caller should not free.
The caller is expected to have checked that tag->items is not NULL.
-Returns -1 on error, 1 if the item was not in the database, and 0 if the
-item was not in the database.
+Returns NULL on error.
*/
static struct ApeItem * ApeTag__get_item(struct ApeTag *tag, const char *key) {
int ret = 0;
@@ -1633,11 +1640,10 @@ static struct ApeItem * ApeTag__get_item(struct ApeTag *tag, const char *key) {
}
/*
-Update the passed in ***items pointer to point to a new array of ApeItem*
-pointers, which the caller is responsible for freeing.
+Return an array of ApeItem * for all items in the tag database,
+which the caller is responsible for freeing.
-Returns <0 on error, 1 if the tag has no items, and 0 if the
-array was set sucessfully.
+Returns NULL on error.
*/
static struct ApeItem ** ApeTag__get_items(struct ApeTag *tag, uint32_t *num_items) {
uint32_t nitems = tag->item_count;
@@ -1659,7 +1665,7 @@ static struct ApeItem ** ApeTag__get_items(struct ApeTag *tag, uint32_t *num_ite
if (tag->items == NULL) {
tag->errcode = APETAG_INTERNALERR;
- tag->error = "internal consistency error: num_items > 0 but items is NULL";
+ tag->error = "internal consistency error: item_count > 0 but items is NULL";
free(is);
return NULL;
}
@@ -1670,7 +1676,7 @@ static struct ApeItem ** ApeTag__get_items(struct ApeTag *tag, uint32_t *num_ite
while (tag->items->seq(tag->items, &key_dbt, &value_dbt, R_NEXT) == 0) {
if (i >= nitems) {
tag->errcode = APETAG_INTERNALERR;
- tag->error = "internal consistency error: more items in database than num_items";
+ tag->error = "internal consistency error: more items in database than item_count";
free(is);
return NULL;
}
@@ -1679,7 +1685,7 @@ static struct ApeItem ** ApeTag__get_items(struct ApeTag *tag, uint32_t *num_ite
}
if (i != nitems) {
tag->errcode = APETAG_INTERNALERR;
- tag->error = "internal consistency error: fewer items in database than num_items";
+ tag->error = "internal consistency error: fewer items in database than item_count";
free(is);
return NULL;
}
@@ -1693,6 +1699,39 @@ static struct ApeItem ** ApeTag__get_items(struct ApeTag *tag, uint32_t *num_ite
}
/*
+Iterate over all items in the database, calling the iterator function
+with the given tag, the current item, and the given data pointer.
+
+Returns 0 if iteration completes successfully, 1 if iteration is stopped
+early, -1 on error.
+*/
+static int ApeTag__iter_items(struct ApeTag *tag, int iterator(struct ApeTag *tag, struct ApeItem *item, void *data), void *data) {
+ if (tag->item_count > 0) {
+ DBT key_dbt, value_dbt;
+
+ if (tag->items == NULL) {
+ tag->errcode = APETAG_INTERNALERR;
+ tag->error = "internal consistency error: item_count > 0 but items is NULL";
+ return -1;
+ }
+
+ /* Call iterator with each item in the database */
+ if (tag->items->seq(tag->items, &key_dbt, &value_dbt, R_FIRST) == 0) {
+ if (iterator(tag, *(struct ApeItem **)(value_dbt.data), data) != 0) {
+ return 1;
+ }
+ while (tag->items->seq(tag->items, &key_dbt, &value_dbt, R_NEXT) == 0) {
+ if (iterator(tag, *(struct ApeItem **)(value_dbt.data), data) != 0) {
+ return 1;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
Local ASCII-only version of strncasecmp, since default strncasecmp may
depend on the locale, and this version is only called with APE item keys
(which are limited to ASCII).
View
@@ -69,6 +69,8 @@ int ApeTag_update(struct ApeTag *tag);
struct ApeItem * ApeTag_get_item(struct ApeTag *tag, const char *key);
struct ApeItem ** ApeTag_get_items(struct ApeTag *tag, uint32_t *item_count);
+int ApeTag_iter_items(struct ApeTag *tag, int iterator(struct ApeTag *tag, struct ApeItem *item, void *data), void *data);
+
uint32_t ApeTag_size(struct ApeTag *tag);
uint32_t ApeTag_item_count(struct ApeTag *tag);
uint32_t ApeTag_file_item_count(struct ApeTag *tag);
View
@@ -26,6 +26,7 @@ int test_ApeTag__strcasecpy(void);
int test_ApeItem__parse_track(void);
int test_ApeItem__compare(void);
int test_ApeTag__lookup_genre(void);
+int test_ApeTag_iter_items(struct ApeTag *tag, struct ApeItem *item, void *data);
#ifndef TEST_TAGS_DIR
# define TEST_TAGS_DIR "test/tags"
@@ -659,12 +660,30 @@ int test_bad_tags(void) {
return 0;
}
+struct iter_items {
+ struct ApeTag *tag;
+ struct ApeItem *items[64];
+ int times_called;
+};
+
+int test_ApeTag_iter_items(struct ApeTag *tag, struct ApeItem *item, void *data) {
+ struct iter_items *ii = data;
+ ii->tag = tag;
+ ii->items[ii->times_called] = item;
+ if (ii->times_called >= 0) {
+ ii->times_called++;
+ return 0;
+ }
+ return 1;
+}
+
int test_ApeTag_add_remove_clear_items_update(void) {
struct ApeTag *tag;
FILE *file;
struct ApeItem *item;
struct ApeItem *check_item;
struct ApeItem **items;
+ struct iter_items data;
uint32_t items_size;
int i;
@@ -712,6 +731,19 @@ int test_ApeTag_add_remove_clear_items_update(void) {
CHECK(memcmp(items[0]->value, "VALUE", 5) == 0);
free(items);
+ memset(&data, 0, sizeof(data));
+ CHECK(ApeTag_iter_items(tag, test_ApeTag_iter_items, &data) == 0);
+ CHECK(data.times_called == 1);
+ CHECK(data.tag == tag);
+ CHECK(data.items[0]->size == 5);
+ CHECK(data.items[0]->flags == 0);
+ CHECK(strcmp(data.items[0]->key, "ALBUM") == 0);
+ CHECK(memcmp(data.items[0]->value, "VALUE", 5) == 0);
+
+ data.times_called = -1;
+ CHECK(ApeTag_iter_items(tag, test_ApeTag_iter_items, &data) == 1);
+ CHECK(data.times_called == -1);
+
/* ensure we don't crash if we don't care about the item count */
items = ApeTag_get_items(tag, NULL);
CHECK(items != NULL);
@@ -779,6 +811,10 @@ int test_ApeTag_add_remove_clear_items_update(void) {
/* Check adding more items than allowed */
CHECK(ApeTag_clear_items(tag) == 0);
for (i=0; i < 64; i++) {
+ memset(&data, 0, sizeof(data));
+ CHECK(ApeTag_iter_items(tag, test_ApeTag_iter_items, &data) == 0);
+ CHECK(data.times_called == i);
+
CHECK(item = malloc(sizeof(struct ApeItem)));
CHECK(item->key = malloc(6));
CHECK(item->value = malloc(3));

0 comments on commit c17a858

Please sign in to comment.