This source file includes following definitions.
- id3_tag_get_frame
- id3_tag_get_numframes
- id3_frame_id
- context_create
- context_destroy
- context_addref
- context_get
- context_get_by_name
- context_delref
- str2int
- str2uint
- destructor_data
- destructor_context
- get_options
- extract_pic
- get_loader
- write_tags
- load
- formats
#include "loader_common.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <id3tag.h>
#if ! defined (__STDC_VERSION__) || __STDC_VERSION__ < 199901L
#if __GNUC__ >= 2
#define inline __inline__
#else
#define inline
#endif
#endif
#ifdef __GNUC__
#define UNLIKELY(exp) __builtin_expect ((exp), 0)
#else
#define UNLIKELY(exp) (exp)
#endif
typedef struct context {
int id;
char *filename;
struct id3_tag *tag;
int refcount;
struct context *next;
} context;
static context *id3_ctxs = NULL;
static inline struct id3_frame *
id3_tag_get_frame(struct id3_tag *tag, size_t index)
{
return tag->frames[index];
}
static inline size_t
id3_tag_get_numframes(struct id3_tag *tag)
{
return tag->nframes;
}
static inline char const *
id3_frame_id(struct id3_frame *frame)
{
return frame->id;
}
static context *
context_create(const char *filename)
{
context *node = (context *) malloc(sizeof(context));
context *ptr, *last;
int last_id = INT_MAX;
node->refcount = 1;
{
struct id3_file *file;
struct id3_tag *tag;
unsigned int i;
file = id3_file_open(filename, ID3_FILE_MODE_READONLY);
if (!file)
{
fprintf(stderr, "Unable to open tagged file %s: %s\n",
filename, strerror(errno));
goto fail_free;
}
tag = id3_file_tag(file);
if (!tag)
{
fprintf(stderr, "Unable to find ID3v2 tags in file %s\n", filename);
id3_file_close(file);
goto fail_free;
}
node->tag = id3_tag_new();
for (i = 0; i < id3_tag_get_numframes(tag); i++)
if (!strcmp(id3_frame_id(id3_tag_get_frame(tag, i)), "APIC"))
id3_tag_attachframe(node->tag, id3_tag_get_frame(tag, i));
id3_file_close(file);
}
node->filename = strdup(filename);
if (!id3_ctxs)
{
node->id = 1;
node->next = NULL;
id3_ctxs = node;
return node;
}
ptr = id3_ctxs;
last = NULL;
while (UNLIKELY(ptr && (ptr->id + 1) >= last_id))
{
last_id = ptr->id;
last = ptr;
ptr = ptr->next;
}
if (UNLIKELY(!ptr))
{
fprintf(stderr, "Too many open ID3 contexts\n");
goto fail_close;
}
node->id = ptr->id + 1;
if (UNLIKELY(! !last))
{
node->next = last->next;
last->next = node;
}
else
{
node->next = id3_ctxs;
id3_ctxs = node;
}
return node;
fail_close:
free(node->filename);
id3_tag_delete(node->tag);
fail_free:
free(node);
return NULL;
}
static void
context_destroy(context * ctx)
{
id3_tag_delete(ctx->tag);
free(ctx->filename);
free(ctx);
}
static inline void
context_addref(context * ctx)
{
ctx->refcount++;
}
static context *
context_get(int id)
{
context *ptr = id3_ctxs;
while (ptr)
{
if (ptr->id == id)
{
context_addref(ptr);
return ptr;
}
ptr = ptr->next;
}
fprintf(stderr, "No context by handle %d found\n", id);
return NULL;
}
static context *
context_get_by_name(const char *name)
{
context *ptr = id3_ctxs;
while (ptr)
{
if (!strcmp(name, ptr->filename))
{
context_addref(ptr);
return ptr;
}
ptr = ptr->next;
}
return NULL;
}
static void
context_delref(context * ctx)
{
ctx->refcount--;
if (ctx->refcount <= 0)
{
context *last = NULL, *ptr = id3_ctxs;
while (ptr)
{
if (ptr == ctx)
{
if (last)
last->next = ctx->next;
else
id3_ctxs = ctx->next;
context_destroy(ctx);
return;
}
last = ptr;
ptr = ptr->next;
}
}
}
static int
str2int(char *str, int old)
{
long index;
errno = 0;
index = strtol(str, NULL, 10);
return ((errno || index > INT_MAX) ? old : (int)index);
}
static size_t
str2uint(char *str, size_t old)
{
unsigned long index;
errno = 0;
index = strtoul(str, NULL, 10);
return ((errno || index > UINT_MAX) ? old : (size_t) index);
}
static void
destructor_data(ImlibImage * im, void *data)
{
free(data);
}
static void
destructor_context(ImlibImage * im, void *data)
{
context_delref((context *) data);
}
typedef struct lopt {
context *ctx;
size_t index;
int traverse;
char cache_level;
} lopt;
static char
get_options(lopt * opt, ImlibImage * im)
{
size_t handle = 0, index = 0, traverse = 0;
context *ctx;
if (im->key)
{
char *key = strdup(im->key);
char *tok = strtok(key, ",");
traverse = 0;
while (tok)
{
char *value = strchr(tok, '=');
if (!value)
{
value = tok;
tok = "index";
}
else
{
*value = '\0';
value++;
}
if (!strcasecmp(tok, "index"))
index = str2uint(value, index);
else if (!strcasecmp(tok, "context"))
handle = str2uint(value, handle);
else if (!strcasecmp(tok, "traverse"))
traverse = str2int(value, traverse);
tok = strtok(NULL, ",");
}
free(key);
}
else
traverse = 1;
if (!handle)
{
ImlibImageTag *htag = __imlib_GetTag(im, "context");
if (htag && htag->val)
handle = htag->val;
}
if (handle)
ctx = context_get(handle);
else if (!(ctx = context_get_by_name(im->real_file)) &&
!(ctx = context_create(im->real_file)))
return 0;
if (!index)
{
ImlibImageTag *htag = __imlib_GetTag(im, "index");
if (htag && htag->val)
index = htag->val;
}
if (index < 0 || index > id3_tag_get_numframes(ctx->tag) ||
(index == 0 && id3_tag_get_numframes(ctx->tag) < 1))
{
if (index)
fprintf(stderr, "No picture frame # %d found\n", index);
context_delref(ctx);
return 0;
}
if (!index)
index = 1;
opt->ctx = ctx;
opt->index = index;
opt->traverse = traverse;
opt->cache_level = (id3_tag_get_numframes(ctx->tag) > 1 ? 1 : 0);
return 1;
}
static int
extract_pic(struct id3_frame *frame, int dest)
{
union id3_field *field;
unsigned char const *data;
id3_length_t length;
int done = 0;
field = id3_frame_field(frame, 4);
data = id3_field_getbinarydata(field, &length);
if (!data)
{
fprintf(stderr, "No image data found for frame\n");
return 0;
}
while (length > 0)
{
ssize_t res;
if ((res = write(dest, data + done, length)) < 0)
{
if (errno == EINTR)
continue;
perror("Unable to write to file");
return 0;
}
length -= res;
done += res;
}
return 1;
}
#define EXT_LEN 14
static char
get_loader(lopt * opt, ImlibLoader ** loader)
{
union id3_field *field;
char const *data;
char ext[EXT_LEN + 2];
ext[EXT_LEN + 1] = '\0';
ext[0] = '.';
field = id3_frame_field(id3_tag_get_frame(opt->ctx->tag, opt->index - 1), 1);
data = (char const *)id3_field_getlatin1(field);
if (!data)
{
fprintf(stderr, "No mime type data found for image frame\n");
return 0;
}
if (strncasecmp(data, "image/", 6))
{
if (!strcmp(data, "-->"))
{
*loader = NULL;
return 1;
}
fprintf(stderr,
"Picture frame with unknown mime-type \'%s\' found\n", data);
return 0;
}
strncpy(ext + 1, data + 6, EXT_LEN);
if (!(*loader = __imlib_FindBestLoaderForFile(ext, 0)))
{
fprintf(stderr, "No loader found for extension %s\n", ext);
return 0;
}
return 1;
}
static char *id3_pic_types[] = {
"Other",
"32x32 pixels file icon",
"Other file icon",
"Cover (front)",
"Cover (back)",
"Leaflet page",
"Media",
"Lead artist/lead performer/soloist",
"Artist/performer",
"Conductor",
"Band/Orchestra",
"Composer",
"Lyricist/text writer",
"Recording Location",
"During recording",
"During performance",
"Movie/video screen capture",
"A bright coloured fish",
"Illustration",
"Band/artist logotype",
"Publisher/Studio logotype"
};
#define NUM_OF_ID3_PIC_TYPES \
(sizeof(id3_pic_types) / sizeof(id3_pic_types[0]))
static char *id3_text_encodings[] = {
"ISO-8859-1",
"UTF-16 encoded Unicode with BOM",
"UTF-16BE encoded Unicode without BOM",
"UTF-8 encoded Unicode"
};
#define NUM_OF_ID3_TEXT_ENCODINGS \
(sizeof(id3_text_encodings) / sizeof(id3_text_encodings[0]))
static void
write_tags(ImlibImage * im, lopt * opt)
{
struct id3_frame *frame = id3_tag_get_frame(opt->ctx->tag, opt->index - 1);
union id3_field *field;
int num_data;
char *data;
if ((field = id3_frame_field(frame, 1)) &&
(data = (char *)id3_field_getlatin1(field)))
__imlib_AttachTag(im, "mime-type", 0, strdup(data), destructor_data);
if ((field = id3_frame_field(frame, 3)) &&
(data = (char *)id3_field_getstring(field)))
{
size_t length;
char *dup;
id3_ucs4_t *ptr = (id3_ucs4_t *) data;
while (*ptr)
ptr++;
length = (ptr - (id3_ucs4_t *) data + 1) * sizeof(id3_ucs4_t);
dup = (char *)malloc(length);
memcpy(dup, data, length);
__imlib_AttachTag(im, "id3-description", 0, dup, destructor_data);
}
if (field = id3_frame_field(frame, 0))
__imlib_AttachTag(im, "id3-description-text-encoding",
(num_data = (int)id3_field_gettextencoding(field)),
num_data < NUM_OF_ID3_TEXT_ENCODINGS ?
id3_text_encodings[num_data] : NULL, NULL);
if (field = id3_frame_field(frame, 2))
__imlib_AttachTag(im, "id3-picture-type",
(num_data = id3_field_getint(field)),
num_data < NUM_OF_ID3_PIC_TYPES ?
id3_pic_types[num_data] : NULL, NULL);
__imlib_AttachTag(im, "count", id3_tag_get_numframes(opt->ctx->tag),
NULL, NULL);
if (opt->cache_level)
{
context_addref(opt->ctx);
__imlib_AttachTag(im, "context", opt->ctx->id,
opt->ctx, destructor_context);
}
__imlib_AttachTag(im, "index", opt->index, NULL, NULL);
if (opt->traverse)
{
char *buf = NULL;
if ((opt->index + opt->traverse)
<= id3_tag_get_numframes(opt->ctx->tag)
&& (opt->index + opt->traverse) > 0)
{
buf = (char *)malloc((strlen(im->real_file) + 50) * sizeof(char));
sprintf(buf, "%s:index=%d,traverse=%d", im->real_file,
opt->index + opt->traverse, opt->traverse);
}
__imlib_AttachTag(im, "next", 0, buf, destructor_data);
}
}
char
load(ImlibImage * im, ImlibProgressFunction progress,
char progress_granularity, char immediate_load)
{
ImlibLoader *loader;
lopt opt;
int res;
struct stat st;
assert(im);
if (stat(im->real_file, &st) < 0)
return 0;
if (!get_options(&opt, im))
return 0;
if (!get_loader(&opt, &loader))
goto fail_context;
if (loader)
{
char *ofile, tmp[] = "/tmp/imlib2_loader_id3-XXXXXX";
int dest;
if ((dest = mkstemp(tmp)) < 0)
{
fprintf(stderr, "Unable to create a temporary file\n");
goto fail_context;
}
res = extract_pic(id3_tag_get_frame(opt.ctx->tag, opt.index - 1), dest);
close(dest);
if (!res)
{
unlink(tmp);
goto fail_context;
}
ofile = im->real_file;
im->real_file = strdup(tmp);
res = loader->load(im, progress, progress_granularity, immediate_load);
free(im->real_file);
im->real_file = ofile;
unlink(tmp);
}
else
{
union id3_field *field;
id3_length_t length;
char const *data;
char *url, *file, *ofile;
field = id3_frame_field
(id3_tag_get_frame(opt.ctx->tag, opt.index - 1), 4);
data = (char const *)id3_field_getbinarydata(field, &length);
if (!data || !length)
{
fprintf(stderr, "No link image URL present\n");
goto fail_context;
}
url = (char *)malloc((length + 1) * sizeof(char));
strncpy(url, data, length);
url[length] = '\0';
file = (strncmp(url, "file://", 7) ? url : url + 7);
if (!(loader = __imlib_FindBestLoaderForFile(file, 0)))
{
fprintf(stderr, "No loader found for file %s\n", file);
free(url);
goto fail_context;
}
ofile = im->real_file;
im->real_file = file;
res = loader->load(im, progress, progress_granularity, immediate_load);
if (!im->loader)
__imlib_AttachTag(im, "id3-link-url", 0, url, destructor_data);
else
free(url);
im->real_file = ofile;
}
if (!im->loader)
write_tags(im, &opt);
#ifdef DEBUG
if (!im->loader)
{
ImlibImageTag *cur = im->tags;
fprintf(stderr, "Tags for file %s:\n", im->file);
while (cur)
{
fprintf(stderr, "\t%s: (%d) %s\n", cur->key,
cur->val, (char *)cur->data);
cur = cur->next;
}
}
#endif
context_delref(opt.ctx);
return res;
fail_context:
context_delref(opt.ctx);
return 0;
}
void
formats(ImlibLoader * l)
{
char *list_formats[] = { "mp3" };
int i;
l->num_formats = sizeof(list_formats) / sizeof(char *);
l->formats = (char **)malloc(sizeof(char *) * l->num_formats);
for (i = 0; i < l->num_formats; i++)
l->formats[i] = strdup(list_formats[i]);
}