revamp plugin_common to use utf-8 instead of wchar_t as the internal comment format

This commit is contained in:
Josh Coalson
2004-12-30 03:38:37 +00:00
parent b990022c95
commit e40480d300
6 changed files with 381 additions and 808 deletions

View File

@@ -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 <config.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h> /* for strlen() and memcpy() */
#include "canonical_tag.h"
#include "vorbiscomment.h"
#include "FLAC/assert.h"
#include "FLAC/metadata.h"
#include <wchar.h>
/*
* 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 <drepper@gnu.ai.mit.edu>, 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 <wchar.h>
#include <string.h>
#include <stdlib.h>
/* 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 <wctype.h>
#include <wchar.h>
#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);
}

View File

@@ -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

307
src/plugin_common/tags.c Normal file
View File

@@ -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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#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<end; chars++) {
const unsigned n = local__utf8len(s);
if (n == 0)
return 0;
s += n;
}
FLAC__ASSERT(s == end);
}
/* allocate */
out = (FLAC__uint16*)malloc(chars * sizeof(FLAC__uint16));
if (0 == out) {
FLAC__ASSERT(0);
return 0;
}
/* convert */
{
FLAC__uint16 *u = out;
for ( ; chars; chars--)
src += local__utf8_to_ucs2(src, u++);
}
return out;
}
static __inline unsigned local__ucs2len(FLAC__uint16 ucs2)
{
if (ucs2 < 0x0080)
return 1;
else if (ucs2 < 0x0800)
return 2;
else
return 3;
}
static __inline unsigned local__ucs2_to_utf8(FLAC__uint16 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;
}
}
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;
}

74
src/plugin_common/tags.h Normal file
View File

@@ -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

View File

@@ -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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#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;
}

View File

@@ -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