diff --git a/src/plugin_common/canonical_tag.c b/src/plugin_common/canonical_tag.c deleted file mode 100644 index c8b11543..00000000 --- a/src/plugin_common/canonical_tag.c +++ /dev/null @@ -1,582 +0,0 @@ -/* plugin_common - Routines common to several plugins - * Copyright (C) 2002,2003,2004 Josh Coalson - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include /* for strlen() and memcpy() */ - -#include "canonical_tag.h" -#include "vorbiscomment.h" -#include "FLAC/assert.h" -#include "FLAC/metadata.h" - -#include - -/* - * Here lies hackage to get any missing wide character string functions we - * need. The fallback implementations here are from glibc. - */ - -#if !defined(_MSC_VER) && !defined(HAVE_WCSDUP) -/* Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Ulrich Drepper , 1995. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ - -#include -#include -#include - - -/* Duplicate S, returning an identical malloc'd string. */ -wchar_t * -wcsdup (s) - const wchar_t *s; -{ - size_t len = (__wcslen (s) + 1) * sizeof (wchar_t); - void *new = malloc (len); - - if (new == NULL) - return NULL; - - return (wchar_t *) memcpy (new, (void *) s, len); -} -#endif - -#if !defined(_MSC_VER) && !defined(HAVE_WCSCASECMP) -/* Copyright (C) 1991, 1992, 1995, 1996, 1997 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ - -#include -#include - -#ifndef weak_alias -# define __wcscasecmp wcscasecmp -# define TOLOWER(Ch) towlower (Ch) -#else -# ifdef USE_IN_EXTENDED_LOCALE_MODEL -# define __wcscasecmp __wcscasecmp_l -# define TOLOWER(Ch) __towlower_l ((Ch), loc) -# else -# define TOLOWER(Ch) towlower (Ch) -# endif -#endif - -#ifdef USE_IN_EXTENDED_LOCALE_MODEL -# define LOCALE_PARAM , loc -# define LOCALE_PARAM_DECL __locale_t loc; -#else -# define LOCALE_PARAM -# define LOCALE_PARAM_DECL -#endif - -/* Compare S1 and S2, ignoring case, returning less than, equal to or - greater than zero if S1 is lexicographically less than, - equal to or greater than S2. */ -int -__wcscasecmp (s1, s2 LOCALE_PARAM) - const wchar_t *s1; - const wchar_t *s2; - LOCALE_PARAM_DECL -{ - wint_t c1, c2; - - if (s1 == s2) - return 0; - - do - { - c1 = TOLOWER (*s1++); - c2 = TOLOWER (*s2++); - if (c1 == L'\0') - break; - } - while (c1 == c2); - - return c1 - c2; -} -#ifndef __wcscasecmp -weak_alias (__wcscasecmp, wcscasecmp) -#endif -#endif - -/* - * helpers - */ - -/* TODO: should be moved out somewhere? @@@ */ - -wchar_t *FLAC_plugin__convert_ansi_to_wide(const char *src) -{ - int len; - wchar_t *dest; - - FLAC__ASSERT(0 != src); - - len = strlen(src) + 1; - /* copy */ - dest = malloc(len*sizeof(wchar_t)); - if (dest) mbstowcs(dest, src, len); - return dest; -} - -/* TODO: more validation? @@@ */ -static __inline int utf8len(const FLAC__byte *utf8) -{ - FLAC__ASSERT(0 != utf8); - if ((*utf8 & 0x80) == 0) - return 1; - else if ((*utf8 & 0xE0) == 0xC0) - return 2; - else if ((*utf8 & 0xF0) == 0xE0) - return 3; - else return 0; -} - -/* TODO: validation? @@@ */ -static __inline int utf8_to_ucs2(const FLAC__byte *utf8, wchar_t *ucs2) -{ - int len; - FLAC__ASSERT(utf8!=0 && *utf8!=0 && ucs2!=0); - - if (!(len = utf8len(utf8))) return 0; - - if (len == 1) - *ucs2 = *utf8; - else if (len == 2) - *ucs2 = (*utf8 & 0x3F)<<6 | (*(utf8+1) & 0x3F); - else if (len == 3) - *ucs2 = (*utf8 & 0x1F)<<12 | (*(utf8+1) & 0x3F)<<6 | (*(utf8+2) & 0x3F); - else { - FLAC__ASSERT(len == 0); - } - - return len; -} - -wchar_t *FLAC_plugin__convert_utf8_to_ucs2(const char *src, unsigned length) -{ - wchar_t *out, *p; - const char *s; - int len = 0; - /* calculate length */ - for (s=src; length && *s; len++) - { - int l = utf8len(s); - if (!l) break; - s += l; - length -= l; - } - /* allocate */ - len++; - p = out = (wchar_t*)malloc(len * sizeof(wchar_t)); - if (!out) return NULL; - /* convert */ - for (s=src; --len; p++) - { - int l = utf8_to_ucs2(s, p); - /* l==0 is possible, because real conversion */ - /* might do more careful validation */ - if (!l) break; - s += l; - } - *p = 0; - - return out; -} - -static __inline int ucs2len(wchar_t ucs2) -{ - if (ucs2 < 0x0080) - return 1; - else if (ucs2 < 0x0800) - return 2; - else return 3; -} - -static __inline int ucs2_to_utf8(wchar_t ucs2, FLAC__byte *utf8) -{ - if (ucs2 < 0x080) - { - utf8[0] = (FLAC__byte)ucs2; - return 1; - } - else if (ucs2 < 0x800) - { - utf8[0] = 0xc0 | (ucs2 >> 6); - utf8[1] = 0x80 | (ucs2 & 0x3f); - return 2; - } - else - { - utf8[0] = 0xe0 | (ucs2 >> 12); - utf8[1] = 0x80 | ((ucs2 >> 6) & 0x3f); - utf8[2] = 0x80 | (ucs2 & 0x3f); - return 3; - } -} - -char *FLAC_plugin__convert_ucs2_to_utf8(const wchar_t *src) -{ - const wchar_t *s; - char *out, *p; - int len = 0; - FLAC__ASSERT(0 != src); - /* calculate length */ - for (s=src; *s; s++) - len += ucs2len(*s); - /* allocate */ - len++; - p = out = malloc(len); - if (!out) return NULL; - /* convert */ - for (s=src; *s; s++) - { - int l = ucs2_to_utf8(*s, p); - p += l; - } - *p = 0; - - return out; -} - -/* - * init/clear/delete - */ - -FLAC_Plugin__CanonicalTag *FLAC_plugin__canonical_tag_new() -{ - FLAC_Plugin__CanonicalTag *object = (FLAC_Plugin__CanonicalTag*)malloc(sizeof(FLAC_Plugin__CanonicalTag)); - if (object != 0) - FLAC_plugin__canonical_tag_init(object); - return object; -} - -void FLAC_plugin__canonical_tag_delete(FLAC_Plugin__CanonicalTag *object) -{ - FLAC_plugin__canonical_tag_clear(object); - free(object); -} - -void FLAC_plugin__canonical_tag_init(FLAC_Plugin__CanonicalTag *object) -{ - object->head = object->tail = 0; - object->count = 0; -} - -static void FLAC_plugin__canonical_tag_clear_entry(FLAC__tag_entry *entry) -{ - free(entry->name); - free(entry->value); - free(entry); -} - -void FLAC_plugin__canonical_tag_clear(FLAC_Plugin__CanonicalTag *object) -{ - FLAC__tag_entry *entry = object->head; - - while (entry) - { - FLAC__tag_entry *next = entry->next; - FLAC_plugin__canonical_tag_clear_entry(entry); - entry = next; - } - - FLAC_plugin__canonical_tag_init(object); -} - -/* - * internal - */ - -static FLAC__tag_entry *FLAC_plugin__canonical_find(const FLAC_Plugin__CanonicalTag *tag, const wchar_t *name) -{ - FLAC__tag_entry *entry = tag->head; - - while (entry) - { -#if defined _MSC_VER || defined __MINGW32__ -#define FLAC__WCSCASECMP wcsicmp -#else -#define FLAC__WCSCASECMP wcscasecmp -#endif - if (!FLAC__WCSCASECMP(name, entry->name)) -#undef FLAC__WCSCASECMP - break; - entry = entry->next; - } - - return entry; -} - -/* NOTE: does NOT copy strings. takes ownership over passed strings. */ -static void FLAC_plugin__canonical_add_tail(FLAC_Plugin__CanonicalTag *tag, wchar_t *name, wchar_t *value) -{ - FLAC__tag_entry *entry = (FLAC__tag_entry*)malloc(sizeof(FLAC__tag_entry)); - if (!entry) - { - free(name); - free(value); - return; - } - /* init */ - entry->name = name; - entry->value = value; - /* add */ - entry->prev = tag->tail; - if (tag->tail) - tag->tail->next = entry; - tag->tail = entry; - if (!tag->head) - tag->head = entry; - entry->next = 0; - tag->count++; -} - -static void FLAC_plugin__canonical_add_new(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value) -{ - FLAC_plugin__canonical_add_tail(tag, wcsdup(name), wcsdup(value)); -} - -/* NOTE: does NOT copy value, but copies name */ -static void FLAC_plugin__canonical_set_nc(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, wchar_t *value) -{ - FLAC__tag_entry *entry = FLAC_plugin__canonical_find(tag, name); - - if (entry) - { - free(entry->value); - entry->value = value; - } - else FLAC_plugin__canonical_add_tail(tag, wcsdup(name), value); -} - -/* NOTE: does NOT copy strings. takes ownership over passed strings. (except sep!) */ -static void FLAC_plugin__canonical_add_nc(FLAC_Plugin__CanonicalTag *tag, wchar_t *name, wchar_t *value, const wchar_t *sep) -{ - FLAC__tag_entry *entry; - - if (sep && (entry = FLAC_plugin__canonical_find(tag, name))) - { - unsigned newlen = wcslen(entry->value) + wcslen(value) + wcslen(sep) + 1; - wchar_t *newvalue = realloc(entry->value, newlen*sizeof(wchar_t)); - - if (newvalue) - { - entry->value = newvalue; - wcscat(entry->value, sep); - wcscat(entry->value, value); - } - - free(name); - free(value); - } - else FLAC_plugin__canonical_add_tail(tag, name, value); -} - -/* - * manipulation - */ - -void FLAC_plugin__canonical_set(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value) -{ - FLAC_plugin__canonical_set_nc(tag, name, wcsdup(value)); -} - -void FLAC_plugin__canonical_set_new(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value) -{ - FLAC__tag_entry *entry = FLAC_plugin__canonical_find(tag, name); - if (!entry) FLAC_plugin__canonical_add_new(tag, name, value); -} - -void FLAC_plugin__canonical_set_ansi(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const char *value) -{ - wchar_t *val = FLAC_plugin__convert_ansi_to_wide(value); - if (val) FLAC_plugin__canonical_set_nc(tag, name, val); -} - -void FLAC_plugin__canonical_add(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value, const wchar_t *sep) -{ - FLAC__tag_entry *entry; - - if (sep && (entry = FLAC_plugin__canonical_find(tag, name))) - { - unsigned newlen = wcslen(entry->value) + wcslen(value) + wcslen(sep) + 1; - wchar_t *newvalue = realloc(entry->value, newlen*sizeof(wchar_t)); - - if (newvalue) - { - entry->value = newvalue; - wcscat(entry->value, sep); - wcscat(entry->value, value); - } - } - else FLAC_plugin__canonical_add_new(tag, name, value); -} - -void FLAC_plugin__canonical_add_utf8(FLAC_Plugin__CanonicalTag *tag, const char *name, const char *value, unsigned namelen, unsigned vallen, const char *sep) -{ - wchar_t *n = FLAC_plugin__convert_utf8_to_ucs2(name, namelen); - wchar_t *v = FLAC_plugin__convert_utf8_to_ucs2(value, vallen); - wchar_t *s = sep ? FLAC_plugin__convert_utf8_to_ucs2(sep, -1) : 0; - - if (n && v) - { - FLAC_plugin__canonical_add_nc(tag, n, v, s); - } - else - { - if (n) free(n); - if (v) free(v); - } - if (s) free(s); -} - -const wchar_t *FLAC_plugin__canonical_get(const FLAC_Plugin__CanonicalTag *tag, const wchar_t *name) -{ - FLAC__tag_entry *entry = FLAC_plugin__canonical_find(tag, name); - return entry ? entry->value : 0; -} - -FLAC__bool FLAC_plugin__canonical_remove(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name) -{ - FLAC__tag_entry *entry = FLAC_plugin__canonical_find(tag, name); - - if (entry) - { - if (entry->prev) - entry->prev->next = entry->next; - else tag->head = entry->next; - - if (entry->next) - entry->next->prev = entry->prev; - else tag->tail = entry->prev; - - FLAC_plugin__canonical_tag_clear_entry(entry); - tag->count--; - return true; - } - - return false; -} - -void FLAC_plugin__canonical_remove_all(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name) -{ - while (FLAC_plugin__canonical_remove(tag, name)); -} - -char *FLAC_plugin__canonical_get_formatted(FLAC__tag_iterator it) -{ - int len1 = wcslen(it->name); - int len2 = wcslen(it->value); - int len = len1 + len2 + 1; - wchar_t *val = malloc((len+1) * sizeof(wchar_t)); - - if (val) - { - char *res; - - memcpy(val, it->name, len1 * sizeof(wchar_t)); - val[len1] = '='; - memcpy(val+len1+1, it->value, len2 * sizeof(wchar_t)); - val[len] = 0; - - res = FLAC_plugin__convert_ucs2_to_utf8(val); - free(val); - return res; - } - - return NULL; -} - -/* - * enumeration - */ -unsigned FLAC_plugin__canonical_get_count(FLAC_Plugin__CanonicalTag *tag) -{ - return tag->count; -} -FLAC__tag_iterator FLAC_plugin__canonical_first(FLAC_Plugin__CanonicalTag *tag) -{ - return tag->head; -} -FLAC__tag_iterator FLAC_plugin__canonical_next(FLAC__tag_iterator it) -{ - return it->next; -} -wchar_t *FLAC_plugin__canonical_get_name(FLAC__tag_iterator it) -{ - return it->name; -} -wchar_t *FLAC_plugin__canonical_get_value(FLAC__tag_iterator it) -{ - return it->value; -} - -/* - * merging - */ - -void FLAC_plugin__canonical_tag_merge(FLAC_Plugin__CanonicalTag *dest, const FLAC_Plugin__CanonicalTag *src) -{ - FLAC__tag_entry *entry = src->head; - - while (entry) - { - FLAC_plugin__canonical_set_new(dest, entry->name, entry->value); - entry = entry->next; - } -} - -void FLAC_plugin__canonical_tag_get_combined(const char *filename, FLAC_Plugin__CanonicalTag *tag, const char *sep) -{ - FLAC_plugin__vorbiscomment_get(filename, tag, sep); -} diff --git a/src/plugin_common/canonical_tag.h b/src/plugin_common/canonical_tag.h deleted file mode 100644 index f792537e..00000000 --- a/src/plugin_common/canonical_tag.h +++ /dev/null @@ -1,88 +0,0 @@ -/* plugin_common - Routines common to several plugins - * Copyright (C) 2002,2003,2004 Josh Coalson - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef FLAC__PLUGIN_COMMON__CANONICAL_TAG_H -#define FLAC__PLUGIN_COMMON__CANONICAL_TAG_H - -#include "FLAC/ordinals.h" - -/* TODO: splay tree? */ -typedef struct tagFLAC__tag_entry FLAC__tag_entry; -struct tagFLAC__tag_entry -{ - FLAC__tag_entry *next, *prev; - /* TODO: name in ascii? */ - wchar_t *name; - wchar_t *value; -}; - -typedef struct { - FLAC__tag_entry *head, *tail; - unsigned count; -} FLAC_Plugin__CanonicalTag; - - -typedef FLAC__tag_entry *FLAC__tag_iterator; - -FLAC_Plugin__CanonicalTag *FLAC_plugin__canonical_tag_new(); -void FLAC_plugin__canonical_tag_init(FLAC_Plugin__CanonicalTag *); -void FLAC_plugin__canonical_tag_clear(FLAC_Plugin__CanonicalTag *); -void FLAC_plugin__canonical_tag_delete(FLAC_Plugin__CanonicalTag *); - -/* note that multiple fields with the same name are allowed. - * set - adds field if it's not present yet, or replaces - * existing field if it's present. - */ -void FLAC_plugin__canonical_set(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value); -void FLAC_plugin__canonical_set_ansi(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const char *value); -/* set_new - only adds field if it's not present yet. */ -void FLAC_plugin__canonical_set_new(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value); -/* add - adds field if it's not present yet, or merges value with existing - * field, if it's present. (sep - separator string to use when merging; - * if sep==NULL no merging occurs - always adds new field) - */ -void FLAC_plugin__canonical_add(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value, const wchar_t *sep); -void FLAC_plugin__canonical_add_utf8(FLAC_Plugin__CanonicalTag *tag, const char *name, const char *value, unsigned namelen, unsigned vallen, const char *sep); /* 'namelen'/'vallen' may be (unsigned)(-1) if 'name'/'value' is NUL-terminated */ - -/* gets value of the first field with the given name (NULL if field not found) */ -const wchar_t *FLAC_plugin__canonical_get(const FLAC_Plugin__CanonicalTag *tag, const wchar_t *name); -/* removes first field with the given name. - * (returns 'true' if deleted, 'false' if not found) - */ -FLAC__bool FLAC_plugin__canonical_remove(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name); -/* removes all fields with the given name. */ -void FLAC_plugin__canonical_remove_all(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name); - -/* enumeration */ -unsigned FLAC_plugin__canonical_get_count(FLAC_Plugin__CanonicalTag *tag); -FLAC__tag_iterator FLAC_plugin__canonical_first(FLAC_Plugin__CanonicalTag *tag); -FLAC__tag_iterator FLAC_plugin__canonical_next(FLAC__tag_iterator it); -wchar_t *FLAC_plugin__canonical_get_name(FLAC__tag_iterator it); -wchar_t *FLAC_plugin__canonical_get_value(FLAC__tag_iterator it); - -/* returns a new string containing the current entry in UTF-8 in "NAME=VALUE" form */ -char *FLAC_plugin__canonical_get_formatted(FLAC__tag_iterator it); - -void FLAC_plugin__canonical_tag_merge(FLAC_Plugin__CanonicalTag *dest, const FLAC_Plugin__CanonicalTag *src); - -/* helpers */ -wchar_t *FLAC_plugin__convert_ansi_to_wide(const char *src); -wchar_t *FLAC_plugin__convert_utf8_to_ucs2(const char *src, unsigned length); /* 'length' may be (unsigned)(-1) if 'src' is NUL-terminated */ -char *FLAC_plugin__convert_ucs2_to_utf8(const wchar_t *src); - -#endif diff --git a/src/plugin_common/tags.c b/src/plugin_common/tags.c new file mode 100644 index 00000000..1de2a8a0 --- /dev/null +++ b/src/plugin_common/tags.c @@ -0,0 +1,307 @@ +/* plugin_common - Routines common to several plugins + * Copyright (C) 2002,2003,2004 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "tags.h" +#include "FLAC/assert.h" +#include "FLAC/metadata.h" + + +static __inline unsigned local__wide_strlen(const FLAC__uint16 *s) +{ + unsigned n = 0; + while(*s++) + n++; + return n; +} + +static __inline unsigned local__utf8len(const FLAC__byte *utf8) +{ + FLAC__ASSERT(0 != utf8); + if ((utf8[0] & 0x80) == 0) + return 1; + else if ((utf8[0] & 0xE0) == 0xC0 && (utf8[1] & 0xC0) == 0x80) + return 2; + else if ((utf8[0] & 0xF0) == 0xE0 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80) + return 3; + else + return 0; +} + +static __inline unsigned local__utf8_to_ucs2(const FLAC__byte *utf8, FLAC__uint16 *ucs2) +{ + const unsigned len = local__utf8len(utf8); + + FLAC__ASSERT(0 != ucs2); + + if (len == 1) + *ucs2 = *utf8; + else if (len == 2) + *ucs2 = (*utf8 & 0x3F)<<6 | (*(utf8+1) & 0x3F); + else if (len == 3) + *ucs2 = (*utf8 & 0x1F)<<12 | (*(utf8+1) & 0x3F)<<6 | (*(utf8+2) & 0x3F); + + return len; +} + +static FLAC__uint16 *local__convert_utf8_to_ucs2(const char *src, unsigned length) +{ + FLAC__uint16 *out; + unsigned chars = 0; + + FLAC__ASSERT(0 != src); + + /* calculate length */ + { + const char *s, *end; + for (s=src, end=src+length; s> 6); + utf8[1] = 0x80 | (ucs2 & 0x3f); + return 2; + } + else { + utf8[0] = 0xe0 | (ucs2 >> 12); + utf8[1] = 0x80 | ((ucs2 >> 6) & 0x3f); + utf8[2] = 0x80 | (ucs2 & 0x3f); + return 3; + } +} + +static char *local__convert_ucs2_to_utf8(const FLAC__uint16 *src, unsigned length) +{ + char *out; + unsigned len = 0; + + FLAC__ASSERT(0 != src); + + /* calculate length */ + { + unsigned i; + for (i = 0; i < length; i++) + len += local__ucs2len(src[i]); + } + + /* allocate */ + out = (char*)malloc(len * sizeof(char)); + if (0 == out) + return 0; + + /* convert */ + { + char *u = out; + for ( ; *src; src++) + u += local__ucs2_to_utf8(*src, u); + local__ucs2_to_utf8(*src, u); + } + + return out; +} + + +FLAC__bool FLAC_plugin__tags_get(const char *filename, FLAC__StreamMetadata **tags) +{ + if(!FLAC__metadata_get_tags(filename, tags)) + if(0 == (*tags = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT))) + return false; + return true; +} + +FLAC__bool FLAC_plugin__tags_set(const char *filename, const FLAC__StreamMetadata *tags) +{ + FLAC__Metadata_Chain *chain; + FLAC__Metadata_Iterator *iterator; + FLAC__StreamMetadata *block; + FLAC__bool got_vorbis_comments = false; + FLAC__bool ok; + + if(0 == (chain = FLAC__metadata_chain_new())) + return false; + + if(!FLAC__metadata_chain_read(chain, filename)) { + FLAC__metadata_chain_delete(chain); + return false; + } + + if(0 == (iterator = FLAC__metadata_iterator_new())) { + FLAC__metadata_chain_delete(chain); + return false; + } + + FLAC__metadata_iterator_init(iterator, chain); + + do { + if(FLAC__metadata_iterator_get_block_type(iterator) == FLAC__METADATA_TYPE_VORBIS_COMMENT) + got_vorbis_comments = true; + } while(!got_vorbis_comments && FLAC__metadata_iterator_next(iterator)); + + if(0 == (block = FLAC__metadata_object_clone(tags))) { + FLAC__metadata_chain_delete(chain); + FLAC__metadata_iterator_delete(iterator); + return false; + } + + if(got_vorbis_comments) + ok = FLAC__metadata_iterator_set_block(iterator, block); + else + ok = FLAC__metadata_iterator_insert_block_after(iterator, block); + + FLAC__metadata_iterator_delete(iterator); + + if(ok) { + FLAC__metadata_chain_sort_padding(chain); + ok = FLAC__metadata_chain_write(chain, /*use_padding=*/true, /*preserve_file_stats=*/true); + } + + FLAC__metadata_chain_delete(chain); + + return ok; +} + +void FLAC_plugin__tags_destroy(FLAC__StreamMetadata **tags) +{ + FLAC__metadata_object_delete(*tags); + *tags = 0; +} + +const char *FLAC_plugin__tags_get_tag_utf8(const FLAC__StreamMetadata *tags, const char *name) +{ + const int i = FLAC__metadata_object_vorbiscomment_find_entry_from(tags, /*offset=*/0, name); + return (i < 0? 0 : tags->data.vorbis_comment.comments[i].entry); +} + +FLAC__uint16 *FLAC_plugin__tags_get_tag_ucs2(const FLAC__StreamMetadata *tags, const char *name) +{ + const char *utf8 = FLAC_plugin__tags_get_tag_utf8(tags, name); + if(0 == utf8) + return 0; + return local__convert_utf8_to_ucs2(utf8, strlen(utf8)+1); /* +1 for terminating null */ +} + +int FLAC_plugin__tags_delete_tag(FLAC__StreamMetadata *tags, const char *name) +{ + return FLAC__metadata_object_vorbiscomment_remove_entries_matching(tags, name); +} + +int FLAC_plugin__tags_delete_all(FLAC__StreamMetadata *tags) +{ + int n = (int)tags->data.vorbis_comment.num_comments; + if(n > 0) { + if(!FLAC__metadata_object_vorbiscomment_resize_comments(tags, 0)) + n = -1; + } + return n; +} + +FLAC__bool FLAC_plugin__tags_add_tag_utf8(FLAC__StreamMetadata *tags, const char *name, const char *value, const char *separator) +{ + int i; + + FLAC__ASSERT(0 != tags); + FLAC__ASSERT(0 != name); + FLAC__ASSERT(0 != value); + + if(separator && (i = FLAC__metadata_object_vorbiscomment_find_entry_from(tags, /*offset=*/0, name)) >= 0) { + FLAC__StreamMetadata_VorbisComment_Entry *entry = tags->data.vorbis_comment.comments+i; + const size_t value_len = strlen(value); + const size_t separator_len = strlen(separator); + FLAC__byte *new_entry; + if(0 == (new_entry = realloc(entry->entry, entry->length + value_len + separator_len + 1))) + return false; + memcpy(new_entry+entry->length, separator, separator_len); + entry->length += separator_len; + memcpy(new_entry+entry->length, value, value_len); + entry->length += value_len; + new_entry[entry->length] = '\0'; + entry->entry = new_entry; + } + else { + FLAC__StreamMetadata_VorbisComment_Entry entry; + if(!FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, name, value)) + return false; + FLAC__metadata_object_vorbiscomment_append_comment(tags, entry, /*copy=*/false); + } + return true; +} + +FLAC__bool FLAC_plugin__tags_set_tag_ucs2(FLAC__StreamMetadata *tags, const char *name, const FLAC__uint16 *value, FLAC__bool replace_all) +{ + FLAC__StreamMetadata_VorbisComment_Entry entry; + + FLAC__ASSERT(0 != tags); + FLAC__ASSERT(0 != name); + FLAC__ASSERT(0 != value); + + { + char *utf8 = local__convert_ucs2_to_utf8(value, local__wide_strlen(value)+1); /* +1 for the terminating null */ + if(0 == utf8) + return false; + if(!FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, name, utf8)) { + free(utf8); + return false; + } + free(utf8); + } + if(!FLAC__metadata_object_vorbiscomment_replace_comment(tags, entry, replace_all, /*copy=*/false)) + return false; + return true; +} diff --git a/src/plugin_common/tags.h b/src/plugin_common/tags.h new file mode 100644 index 00000000..379d2440 --- /dev/null +++ b/src/plugin_common/tags.h @@ -0,0 +1,74 @@ +/* plugin_common - Routines common to several plugins + * Copyright (C) 2002,2003,2004 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef FLAC__PLUGIN_COMMON__TAGS_H +#define FLAC__PLUGIN_COMMON__TAGS_H + +#include "FLAC/format.h" + +FLAC__bool FLAC_plugin__tags_get(const char *filename, FLAC__StreamMetadata **tags); +FLAC__bool FLAC_plugin__tags_set(const char *filename, const FLAC__StreamMetadata *tags); + +/* + * Deletes the tags object and sets '*tags' to NULL. + */ +void FLAC_plugin__tags_destroy(FLAC__StreamMetadata **tags); + +/* + * Gets the value (in UTF-8) of the first tag with the given name (NULL if no + * such tag exists). + */ +const char *FLAC_plugin__tags_get_tag_utf8(const FLAC__StreamMetadata *tags, const char *name); + +/* + * Gets the value (in UCS-2) of the first tag with the given name (NULL if no + * such tag exists). + * + * NOTE: the returned string is malloc()ed and must be free()d by the caller. + */ +FLAC__uint16 *FLAC_plugin__tags_get_tag_ucs2(const FLAC__StreamMetadata *tags, const char *name); + +/* + * Removes all tags with the given 'name'. Returns the number of tags removed, + * or -1 on memory allocation error. + */ +int FLAC_plugin__tags_delete_tag(FLAC__StreamMetadata *tags, const char *name); + +/* + * Removes all tags. Returns the number of tags removed, or -1 on memory + * allocation error. + */ +int FLAC_plugin__tags_delete_all(FLAC__StreamMetadata *tags); + +/* + * Adds a "name=value" tag to the tags. 'value' must be in UTF-8. If + * 'separator' is non-NULL and 'tags' already contains a tag for 'name', the + * first such tag's value is appended with separator, then value. + */ +FLAC__bool FLAC_plugin__tags_add_tag_utf8(FLAC__StreamMetadata *tags, const char *name, const char *value, const char *separator); + +/* + * Adds a "name=value" tag to the tags. 'value' must be in UCS-2. If 'tags' + * already contains a tag or tags for 'name', then they will be replaced + * according to 'replace_all': if 'replace_all' is false, only the first such + * tag will be replaced; if true, all matching tags will be replaced by the one + * new tag. + */ +FLAC__bool FLAC_plugin__tags_set_tag_ucs2(FLAC__StreamMetadata *tags, const char *name, const FLAC__uint16 *value, FLAC__bool replace_all); + +#endif diff --git a/src/plugin_common/vorbiscomment.c b/src/plugin_common/vorbiscomment.c deleted file mode 100644 index 934b4f89..00000000 --- a/src/plugin_common/vorbiscomment.c +++ /dev/null @@ -1,110 +0,0 @@ -/* plugin_common - Routines common to several plugins - * Copyright (C) 2002,2003,2004 Josh Coalson - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include - -#include "vorbiscomment.h" -#include "FLAC/metadata.h" - - -static void local__add_vcentry(FLAC_Plugin__CanonicalTag *tag, FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *sep) -{ - FLAC__byte *value = memchr(entry->entry, '=', entry->length); - int len; - if (!value) return; - len = value - entry->entry; - value++; - FLAC_plugin__canonical_add_utf8(tag, entry->entry, value, len, entry->length-len-1, sep); -} - -void FLAC_plugin__vorbiscomment_get(const char *filename, FLAC_Plugin__CanonicalTag *tag, const char *sep) -{ - FLAC__Metadata_SimpleIterator *iterator = FLAC__metadata_simple_iterator_new(); - if(0 != iterator) { - if(FLAC__metadata_simple_iterator_init(iterator, filename, /*read_only=*/true, /*preserve_file_stats=*/true)) { - FLAC__bool got_vorbis_comments = false; - do { - if(FLAC__metadata_simple_iterator_get_block_type(iterator) == FLAC__METADATA_TYPE_VORBIS_COMMENT) { - FLAC__StreamMetadata *block = FLAC__metadata_simple_iterator_get_block(iterator); - if(0 != block) { - unsigned i; - const FLAC__StreamMetadata_VorbisComment *vc = &block->data.vorbis_comment; - - for(i = 0; i < vc->num_comments; i++) - local__add_vcentry(tag, vc->comments+i, sep); - - FLAC__metadata_object_delete(block); - got_vorbis_comments = true; - } - } - } while (!got_vorbis_comments && FLAC__metadata_simple_iterator_next(iterator)); - } - FLAC__metadata_simple_iterator_delete(iterator); - } -} - -FLAC__bool FLAC_plugin__vorbiscomment_set(const char *filename, FLAC_Plugin__CanonicalTag *tag) -{ - FLAC__bool got_vorbis_comments = false, result; - FLAC__Metadata_SimpleIterator *iterator = FLAC__metadata_simple_iterator_new(); - FLAC__StreamMetadata *block; - FLAC__tag_iterator it; - unsigned position = 0; - - if (!iterator || !FLAC__metadata_simple_iterator_init(iterator, filename, /*read_only=*/false, /*preserve_file_stats=*/true)) - return false; - - do { - if(FLAC__metadata_simple_iterator_get_block_type(iterator) == FLAC__METADATA_TYPE_VORBIS_COMMENT) - got_vorbis_comments = true; - } while (!got_vorbis_comments && FLAC__metadata_simple_iterator_next(iterator)); - - if(!got_vorbis_comments) { - /* create a new block */ - block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT); - - if(!block) { - FLAC__metadata_simple_iterator_delete(iterator); - return false; - } - } - else - block = FLAC__metadata_simple_iterator_get_block(iterator); - - FLAC__metadata_object_vorbiscomment_resize_comments(block, FLAC_plugin__canonical_get_count(tag)); - - for (it=FLAC_plugin__canonical_first(tag); it; it=FLAC_plugin__canonical_next(it)) - { - FLAC__StreamMetadata_VorbisComment_Entry entry; - /* replace entry */ - entry.entry = FLAC_plugin__canonical_get_formatted(it); - entry.length = strlen(entry.entry); - FLAC__metadata_object_vorbiscomment_set_comment(block, position++, entry, /*copy=*/false); - } - - if (!got_vorbis_comments) - result = FLAC__metadata_simple_iterator_insert_block_after(iterator, block, /*use_padding=*/true); - else - result = FLAC__metadata_simple_iterator_set_block(iterator, block, /*use_padding=*/true); - - FLAC__metadata_object_delete(block); - FLAC__metadata_simple_iterator_delete(iterator); - return result; -} diff --git a/src/plugin_common/vorbiscomment.h b/src/plugin_common/vorbiscomment.h deleted file mode 100644 index f02b5cac..00000000 --- a/src/plugin_common/vorbiscomment.h +++ /dev/null @@ -1,28 +0,0 @@ -/* plugin_common - Routines common to several plugins - * Copyright (C) 2002,2003,2004 Josh Coalson - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef FLAC__PLUGIN_COMMON__VORBISCOMMENT_H -#define FLAC__PLUGIN_COMMON__VORBISCOMMENT_H - -#include "canonical_tag.h" -#include "FLAC/ordinals.h" - -void FLAC_plugin__vorbiscomment_get(const char *filename, FLAC_Plugin__CanonicalTag *tag, const char *sep); -FLAC__bool FLAC_plugin__vorbiscomment_set(const char *filename, FLAC_Plugin__CanonicalTag *tag); - -#endif