aboutsummaryrefslogtreecommitdiff
path: root/libmpio/mplib
diff options
context:
space:
mode:
Diffstat (limited to 'libmpio/mplib')
-rw-r--r--libmpio/mplib/mplib.c1011
-rw-r--r--libmpio/mplib/mplib.h438
-rw-r--r--libmpio/mplib/mplib_paas.c256
-rw-r--r--libmpio/mplib/mplib_s.c1177
-rw-r--r--libmpio/mplib/mplib_s.h153
-rw-r--r--libmpio/mplib/xmalloc.c167
-rw-r--r--libmpio/mplib/xmalloc.h58
7 files changed, 3260 insertions, 0 deletions
diff --git a/libmpio/mplib/mplib.c b/libmpio/mplib/mplib.c
new file mode 100644
index 0000000..afa7066
--- /dev/null
+++ b/libmpio/mplib/mplib.c
@@ -0,0 +1,1011 @@
+/*
+ * mplib - a library that enables you to edit ID3 tags
+ * Copyright (C) 2001,2002 Stefan Podkowinski
+ *
+ * This 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; version 2.1.
+ *
+ * This 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 this library; 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
+
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#elif HAVE_STRINGS_H
+# include <strings.h>
+#endif /*STDC_HEADERS*/
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+# include <sys/types.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+#include "xmalloc.h"
+#include "mplib.h"
+#include "mplib_s.h"
+#include "mplib_s.c"
+
+
+
+/*******************************************************************************************
+ * Extern functions
+ *******************************************************************************************/
+
+
+
+/*******************************************************************************************
+ * Get
+ *******************************************************************************************/
+
+ mpeg_header*
+ mp_get_mpeg_header_from_file(const char* filename)
+ {
+ mpeg_header *ret;
+ int fd;
+
+ if(!filename) return NULL;
+
+ fd = open(filename, O_RDONLY);
+ if(fd == -1) return NULL;
+
+ ret = mp_get_mpeg_header_from_fd(fd);
+ close(fd);
+ return ret;
+ }
+
+
+mpeg_header*
+mp_get_mpeg_header_from_fd(int fd)
+{
+ mpeg_header *h;
+ unsigned char c[5];
+
+ h = XMALLOCD(mpeg_header, "mp_get_mpeg_header_from_fd:h");
+
+ if(id3_lseek_syncword(fd)) goto exit_on_error;
+
+ if(read(fd, c, 4) < 4) goto exit_on_error;
+
+ memset(h, 0, sizeof(h));
+ h->syncword = (c[1] & 240);
+ h->syncword <<= 8;
+ h->syncword |= c[0];
+ h->version = (c[1] & 8) >> 3;
+ h->layer = (c[1] & 6) >> 1;
+ h->protbit = (c[1] & 1);
+ h->bitrate = (c[2] & 240) >> 4;
+ h->samplingfreq = (c[2] & 12) >> 2;
+ h->padbit = (c[2] & 2) >> 1;
+ h->privbit = (c[2] & 1);
+ h->mode = (c[3] & 192) >> 6;
+ h->mode_ext = (c[3] & 48) >> 4;
+ h->copyright = (c[3] & 8) >> 3;
+ h->originalhome = (c[3] & 4) >> 2;
+ h->emphasis = (c[3] & 3);
+
+ return h;
+
+ exit_on_error:
+ xfree(h);
+ return NULL;
+}
+
+char*
+mp_get_str_version(const mpeg_header *h)
+{
+ return h->version == 0 ? "MPEG 2" : "MPEG 1";
+}
+
+char*
+mp_get_str_layer(const mpeg_header *h)
+{
+ switch(h->layer)
+ {
+ case 1: return "Layer III";
+ case 2: return "Layer II";
+ case 3: return "Layer I";
+ default: return "undefined";
+ }
+}
+
+char*
+mp_get_str_bitrate(const mpeg_header *h)
+{
+ char *buf = (char *)xmallocd0(11, "mp_get_str_bitrate:buf");
+
+ if(h->version == 1) /* MPEG 1 */
+ {
+ switch(h->layer)
+ {
+ case 1:
+ snprintf(buf, sizeof buf, "%d kBit/s", br_1_3[h->bitrate]);
+ return buf;
+ case 2:
+ snprintf(buf, sizeof buf, "%d kBit/s", br_1_2[h->bitrate]);
+ return buf;
+ case 3:
+ snprintf(buf, sizeof buf, "%d kBit/s", br_1_1[h->bitrate]);
+ return buf;
+ default:
+ return "undefined";
+ }
+ }
+ else /* MPEG 2 */
+ {
+ switch(h->layer)
+ {
+ case 1:
+ snprintf(buf, sizeof buf, "%d kBit/s", br_2_3[h->bitrate]);
+ return buf;
+ case 2:
+ snprintf(buf, sizeof buf, "%d kBit/s", br_2_2[h->bitrate]);
+ return buf;
+ case 3:
+ snprintf(buf, sizeof buf, "%d kBit/s", br_2_1[h->bitrate]);
+ return buf;
+ default:
+ return "undefined";
+ }
+ }
+}
+
+char*
+mp_get_str_samplingfreq(const mpeg_header *h)
+{
+ if(h->version == 1)
+ {
+ switch(h->samplingfreq)
+ {
+ case 0: return "44100 Hz";
+ case 1: return "48000 Hz";
+ case 2: return "32000 Hz";
+ default: return "undefined";
+ }
+ }
+ else
+ {
+ switch(h->samplingfreq)
+ {
+ case 0: return "22050 Hz";
+ case 1: return "24000 Hz";
+ case 2: return "16000 Hz";
+ default: return "undefined";
+ }
+ }
+}
+
+char*
+mp_get_str_mode(const mpeg_header *h)
+{
+ switch(h->mode)
+ {
+ case 0: return "Stereo";
+ case 1: return "Joint-Stereo";
+ case 2: return "Dual-Channel";
+ case 3: return "Mono";
+ default: return "undefined";
+ }
+}
+
+id3_tag_list*
+mp_get_tag_list_from_file(const char* filename)
+{
+ id3_tag_list *ret;
+ int fd;
+
+ if(!filename) return NULL;
+
+ fd = open(filename, O_RDONLY);
+ if(fd == -1) return NULL;
+
+ ret = mp_get_tag_list_from_fd(fd);
+ close(fd);
+ return ret;
+}
+
+id3_tag_list*
+mp_get_tag_list_from_fd(int fd)
+{
+ id3_tag_list *tag_list = NULL;
+ id3_tag_list *tag_list2 = NULL;
+ id3v2_tag *v2tag = NULL;
+ id3v1_tag *v1tag = NULL;
+ id3_tag *tag = NULL;
+
+ v2tag = id3v2_get_tag(fd);
+ if(v2tag)
+ {
+ tag = XMALLOCD0(id3_tag, "mp_get_tag_list_from_fd:tag");
+ if(v2tag->header->version_minor == 3 || v2tag->header->version_minor == 4)
+ tag->version = 2;
+ else
+ tag->version = -1;
+ tag->tag = v2tag;
+
+ tag_list = XMALLOCD(id3_tag_list, "mp_get_tag_list_from_fd:tag_list");
+ tag_list->tag = tag;
+ tag_list->next = NULL;
+ tag_list->first = tag_list;
+ }
+
+ v1tag = id3v1_get_tag(fd);
+ if(v1tag)
+ {
+ tag = XMALLOCD(id3_tag, "mp_get_tag_list_from_fd:tag");
+ tag->version = 1;
+ tag->tag = v1tag;
+
+ if(tag_list)
+ {
+ tag_list2 = XMALLOCD(id3_tag_list, "mp_get_tag_list_from_fd:tag_list2");
+ tag_list2->tag = tag;
+ tag_list2->next = NULL;
+ tag_list2->first = tag_list;
+ tag_list->next = tag_list2;
+ }
+ else
+ {
+ tag_list = XMALLOCD(id3_tag_list, "mp_get_tag_list_from_fd:tag_list");
+ tag_list->tag = tag;
+ tag_list->next = NULL;
+ tag_list->first = tag_list;
+ }
+ }
+
+ return tag_list;
+}
+
+id3_content*
+mp_get_content(const id3_tag *tag, int field)
+{
+ return mp_get_content_at_pos(tag, field, 0);
+}
+
+id3_content*
+mp_get_content_at_pos(const id3_tag *tag, int field, int pos)
+{
+ int i;
+ char *c;
+ id3_content *ret;
+
+ if(!tag || !tag->tag)
+ {
+ errno = MP_EERROR;
+ return NULL;
+ }
+
+ if(tag->version == 1)
+ {
+ if(pos != 0)
+ {
+ errno = MP_EERROR;
+ return NULL;
+ }
+ else return id3v1_get_content(tag->tag, field);
+ }
+ else if(tag->version == 2)
+ {
+ id3v2_tag *v2 = tag->tag;
+ char *val;
+
+ switch(field)
+ {
+ case MP_ARTIST:
+ return mp_get_content_custom_at_pos(tag, "TPE1", pos);
+ case MP_TITLE:
+ return mp_get_content_custom_at_pos(tag, "TIT2", pos);
+ case MP_ALBUM:
+ return mp_get_content_custom_at_pos(tag, "TALB", pos);
+ case MP_GENRE:
+ return mp_get_content_custom_at_pos(tag, "TCON", pos);
+ case MP_COMMENT:
+ return mp_get_content_custom_at_pos(tag, "COMM", pos);
+ case MP_YEAR:
+ return mp_get_content_custom_at_pos(tag, "TYER", pos);
+ case MP_TRACK:
+ return mp_get_content_custom_at_pos(tag, "TRCK", pos);
+ }
+ errno = MP_EFNF;
+ return NULL;
+ }
+ else
+ {
+ errno = MP_EVERSION;
+ return NULL;
+ }
+}
+
+id3_content*
+mp_get_content_custom(const id3_tag* tag, const char*field)
+{
+ if(!tag)
+ {
+ errno = MP_EERROR;
+ return NULL;
+ }
+ else if(tag->version != 2)
+ {
+ errno = MP_EVERSION;
+ return NULL;
+ }
+
+ return id3v2_get_content_at_pos(tag->tag, field, 0);
+
+}
+
+id3_content*
+mp_get_content_custom_at_pos(const id3_tag* tag, const char*field, int pos)
+{
+ if(!tag)
+ {
+ errno = MP_EERROR;
+ return NULL;
+ }
+ else if(tag->version != 2)
+ {
+ errno = MP_EVERSION;
+ return NULL;
+ }
+
+
+ return id3v2_get_content_at_pos(tag->tag, field, pos);
+}
+
+
+/*******************************************************************************************
+ * Set
+ *******************************************************************************************/
+
+ int
+ mp_set_content(id3_tag* tag, const int field, id3_content* new_content)
+ {
+ id3v1_tag *v1;
+ id3v2_tag *v2;
+
+
+ if(!tag) return MP_EERROR;
+
+ if(tag->version == 2)
+ {
+ return mp_set_content_at_pos(tag, field, new_content, 0);
+ }
+ else if(tag->version == 1)
+ {
+ unsigned char c;
+ char *my_val;
+ int len, j;
+
+ v1 = tag->tag;
+
+ switch(field)
+ {
+
+#define FLD(str1, str2, str3, str4) \
+ case str1:\
+ if(!new_content) v1->str2 = NULL;\
+ else\
+ {\
+ id3_text_content *tc = str4(new_content);\
+ if(strlen(tc->text) > str3 || tc->encoding != ISO_8859_1)\
+ {\
+ mp_convert_to_v2(tag);\
+ mp_free_text_content(tc);\
+ return mp_set_content(tag, field, new_content);\
+ }\
+ \
+ v1->str2 = tc->text;\
+ xfree(tc);\
+ }\
+ break;
+
+ FLD(MP_ARTIST, artist, 30, mp_parse_artist);
+ FLD(MP_TITLE, title, 30, mp_parse_title);
+ FLD(MP_ALBUM, album, 30, mp_parse_album);
+ FLD(MP_YEAR, year, 4, mp_parse_year);
+
+ case MP_COMMENT:
+ if(!new_content) v1->comment = NULL;
+ else
+ {
+ id3_comment_content *tc = mp_parse_comment(new_content);
+ if(strlen(tc->text) > 30 || tc->short_descr || tc->encoding != ISO_8859_1)
+ {
+ mp_convert_to_v2(tag);
+ mp_free_comment_content(tc);
+ return mp_set_content(tag, field, new_content);
+ }
+ v1->comment = xmallocd0(strlen(tc->text)+1,
+ "mp_set_content:v1->comment");
+ memcpy(v1->comment, tc->text, strlen(tc->text));
+ mp_free_comment_content(tc);
+ }
+ break;
+
+ case MP_TRACK:
+ if(!new_content) v1->track = 0;
+ else
+ {
+ id3_text_content *tc = mp_parse_track(new_content);
+#ifdef HAVE_STRTOL
+ errno = 0;
+ j = strtol(tc->text, (char **)NULL, 10);
+ if(errno != ERANGE) v1->track = j;
+ else return MP_EERROR;
+#else
+ v1->track = atoi(tc->text);
+#endif
+ mp_free_text_content(tc);
+ }
+ break;
+
+ case MP_GENRE:
+ if(!new_content) v1->genre = 0xFF;
+ else
+ {
+ int b = 0, i;
+ id3_text_content *tc = mp_parse_genre(new_content);
+ /* i = strlen(tc->text); */
+ for(c = 0; c < GLL; c++) {
+ if(!strcmp(genre_list[c], tc->text))
+ {
+ v1->genre = c;
+ b = 1;
+ }
+ }
+ mp_free_text_content(tc);
+ if(!b)
+ {
+ mp_convert_to_v2(tag);
+ return mp_set_content(tag, field, new_content);
+ }
+ break;
+ }
+ }
+ }
+ else if(tag->version == -1) return MP_EVERSION;
+ else return MP_EFNF;
+
+ return 0;
+ }
+
+int
+mp_set_content_at_pos(id3_tag* tag, const int field, id3_content* new_content, int pos)
+{
+ char* c;
+
+ if(!tag) return MP_EERROR;
+ if(field < MP_ARTIST || field > MP_TRACK) return MP_EFNF;
+
+ if(tag->version == 1 && pos == 0) return mp_set_content(tag, field, new_content);
+
+ switch(field)
+ {
+ case MP_ARTIST: c = "TPE1"; break;
+ case MP_TITLE: c = "TIT2"; break;
+ case MP_ALBUM: c = "TALB"; break;
+ case MP_TRACK: c = "TRCK"; break;
+ case MP_YEAR: c = "TYER"; break;
+ case MP_COMMENT: c = "COMM"; break;
+ case MP_GENRE: c = "TCON"; break;
+ }
+ return mp_set_custom_content_at_pos(tag, c, new_content, pos);
+}
+
+int
+mp_set_custom_content(id3_tag* tag, char* field, id3_content* new_content)
+{
+ return mp_set_custom_content_at_pos(tag, field, new_content, 0);
+}
+
+int
+mp_set_custom_content_at_pos(id3_tag* tag, char* field, id3_content* new_content, int pos)
+{
+ id3v2_tag *v2;
+
+ if(!tag || !field || strlen(field) != 4) return MP_EERROR;
+
+ if(tag->version == 1)
+ {
+ if(mp_convert_to_v2(tag))
+ return MP_EERROR;
+ }
+ else if(tag->version == -1) return MP_EVERSION;
+
+ v2 = (id3v2_tag*)tag->tag;
+ if(!v2->frame_list)
+ {
+ v2->frame_list = XMALLOCD0(id3v2_frame_list,
+ "mp_set_custom_content_at_pos:v2->frame_list");
+ id3_add_frame(v2->frame_list, field, new_content->data, new_content->length);
+ }
+ else
+ {
+ id3v2_frame *frame;
+
+ if((frame = id3_lookup_frame(v2->frame_list, field, pos)))
+ {
+ if(new_content)
+ {
+ long len, len_sync;
+ /* make sync safe */
+ len = new_content->length;
+ len_sync = id3_sync(new_content->data, len);
+
+ xfree(frame->data);
+ frame->data = xmallocd(new_content->length,
+ "mp_set_custom_content_at_pos:frame->data");
+ memcpy(frame->data, new_content->data, new_content->length);
+ frame->status_flag = 0;
+ if(len != len_sync) frame->format_flag = 64;
+ else frame->format_flag = 0;
+ frame->data_size = len_sync;
+ }
+ else id3_remove_frame(v2->frame_list, frame);
+ }
+ else if(pos == 0) id3_add_frame(v2->frame_list, field, new_content->data, new_content->length);
+ else return MP_EFNF;
+ }
+
+ return 0;
+}
+
+/*******************************************************************************************
+ * Write & delete
+ *******************************************************************************************/
+ int
+ mp_write_to_file(const id3_tag_list* tag_list, const char *filename)
+ {
+ int ret;
+ int fd;
+
+ if(!filename) return MP_EERROR;
+
+ fd = open(filename, O_RDWR);
+ if(fd == -1) return MP_EERROR;
+
+ ret = mp_write_to_fd(tag_list, fd);
+ close(fd);
+ return ret;
+ }
+
+
+ int
+ mp_write_to_fd(const id3_tag_list* tag_list, const int fd)
+ {
+ id3_tag *tag;
+ id3v1_tag *v1;
+ id3v2_tag *v2;
+ id3_tag_list *mylist;
+ int ret = 0;
+
+ if(!tag_list) {
+ ret |= id3v1_del_tag(fd);
+ ret |= id3v2_del_tag(fd, NULL);
+ return ret;
+ }
+
+ while(tag_list)
+ {
+ tag = tag_list->tag;
+ if(!tag)
+ {
+ tag_list = tag_list->next;
+ continue;
+ }
+
+ if(tag->version == 1)
+ {
+ id3v1_del_tag(fd);
+ ret |= id3v1_add_tag(fd, tag->tag);
+ }
+ else if(tag->version == 2)
+ {
+ int pad = 0;
+ id3v2_frame_list *frame_list;
+ id3v2_tag *old_v2;
+ id3v2_tag *v2 = tag->tag;
+
+ /* calculate tag size */
+ v2->header->total_tag_size = 10;
+ if(v2->header->has_footer) v2->header->total_tag_size += 10;
+ if(v2->header->has_extended_header) v2->header->total_tag_size += v2->header->extended_header->size;
+ frame_list = v2->frame_list;
+ while(frame_list)
+ {
+ v2->header->total_tag_size += frame_list->data->data_size + 10;
+ frame_list = frame_list->next;
+ }
+
+ /* this is where padding handling takes place */
+ /* we must get the old tag to see if padding can be used */
+ old_v2 = id3v2_get_tag(fd);
+ if(old_v2) {
+ if(v2->header->total_tag_size > old_v2->header->total_tag_size)
+ {
+ /* padding not sufficent */
+ ret |= id3v2_del_tag(fd, old_v2);
+ ret |= id3v2_add_tag(fd, v2, NULL);
+ }
+ else
+ {
+ ret |= id3v2_add_tag(fd, v2, old_v2);
+ }
+ id3v2_free_tag(old_v2);
+ } else {
+ ret |= id3v2_add_tag(fd, v2, NULL);
+ }
+
+ }
+ else
+ {
+ ret |= MP_EVERSION;
+ }
+
+ tag_list = tag_list->next;
+ } /* tag list */
+
+ return ret;
+ }
+
+int
+mp_del_tags_from_file(const char* filename)
+{
+ int ret, fd;
+
+ if(!filename) return 1;
+
+ fd = open(filename, O_RDWR);
+ if(fd == -1) return 1;
+
+ ret = mp_del_tags_from_fd(fd);
+ close(fd);
+ return ret;
+}
+
+int
+mp_del_tags_from_fd(const int fd)
+{
+ int ret = 0;
+
+ ret |= id3v1_del_tag(fd);
+ ret |= id3v2_del_tag(fd, NULL);
+
+ return ret;
+}
+
+int
+mp_del_tags_by_ver_from_file(const char* filename, const int version)
+{
+ int fd, ret;
+
+ if(!filename) return 1;
+
+ fd = open(filename, O_RDWR);
+ if(fd == -1) return 1;
+
+ ret = mp_del_tags_by_ver_from_fd(fd, version);
+ close(fd);
+ return ret;
+}
+
+int
+mp_del_tags_by_ver_from_fd(const int fd, const int version)
+{
+ if(version == 1) return id3v1_del_tag(fd);
+ else if(version == 2) return id3v2_del_tag(fd, NULL);
+ else return MP_EVERSION;
+}
+
+
+
+
+/*******************************************************************************************
+ * Misc
+ *******************************************************************************************/
+
+ int
+ mp_convert_to_v2(id3_tag *tag)
+ {
+ id3v1_tag *v1;
+ id3_tag *tmp;
+ id3_content* content;
+
+ if(tag->version == 2) return 0;
+ else if(tag->version == -1) return MP_EVERSION;
+
+ tmp = mp_alloc_tag_with_version(2);
+
+ v1 = (id3v1_tag*)tag->tag;
+
+ content = mp_assemble_text_content(v1->artist, ISO_8859_1);
+ if(v1->artist) mp_set_content(tmp, MP_ARTIST, content);
+
+ content = mp_assemble_text_content(v1->title, ISO_8859_1);
+ if(v1->title) mp_set_content(tmp, MP_TITLE, content);
+
+ content = mp_assemble_text_content(v1->album, ISO_8859_1);
+ if(v1->album) mp_set_content(tmp, MP_ALBUM, content);
+
+ content = mp_assemble_text_content(v1->year, ISO_8859_1);
+ if(v1->year) mp_set_content(tmp, MP_YEAR, content);
+
+ content = mp_assemble_comment_content(v1->comment, NULL, ISO_8859_1, NULL);
+ if(v1->comment) mp_set_content(tmp, MP_COMMENT, content);
+
+ if(v1->genre != 0xFF)
+ {
+ char *c = xmallocd(strlen(genre_list[v1->genre]) + 1,
+ "mp_convert_to_v2:c");
+ strcpy(c, genre_list[v1->genre]);
+ content = mp_assemble_text_content(c, ISO_8859_1);
+ mp_set_content(tmp, MP_GENRE, content);
+ }
+ if(v1->track > 0)
+ {
+ char *trk = (char *)xmallocd(4, "mp_convert_to_v2:trk");
+ snprintf(trk, 3, "%d", v1->track);
+ trk[3] = 0;
+ content = mp_assemble_text_content(trk, ISO_8859_1);
+ mp_set_content(tmp, MP_TRACK, content);
+ }
+
+ tag->version = 2;
+ tag->tag = tmp->tag;
+
+ id3v1_free_tag(v1);
+ xfree(tmp);
+
+ return 0;
+ }
+
+int
+mp_convert_to_v1(id3_tag *tag)
+{
+ id3v1_tag *v1;
+ id3_tag* tmp;
+ id3_content* content;
+ id3_text_content* tc;
+ id3_comment_content* cc;
+ char* c;
+ int j, k = 0;
+
+ if(tag->version == 1) return 0;
+ else if(tag->version == -1) return MP_EVERSION;
+
+ v1 = XMALLOCD0(id3v1_tag, "mp_convert_to_v1:v1");
+
+ content = mp_get_content(tag, MP_ARTIST);
+ tc = mp_parse_artist(content);
+ v1->artist = tc->text;
+ xfree(tc);
+ mp_free_content(content);
+
+ content = mp_get_content(tag, MP_TITLE);
+ tc = mp_parse_title(content);
+ v1->title = tc->text;
+ xfree(tc);
+ mp_free_content(content);
+
+ content = mp_get_content(tag, MP_ALBUM);
+ tc = mp_parse_album(content);
+ v1->album = tc->text;
+ xfree(tc);
+ mp_free_content(content);
+
+ content = mp_get_content(tag, MP_YEAR);
+ tc = mp_parse_year(content);
+ v1->year = tc->text;
+ xfree(tc);
+ mp_free_content(content);
+
+ content = mp_get_content(tag, MP_COMMENT);
+ cc = mp_parse_comment(content);
+ v1->comment = cc->text;
+ xfree(cc->language);
+ xfree(cc->short_descr);
+ xfree(cc);
+ mp_free_content(content);
+
+ content = mp_get_content(tag, MP_TRACK);
+ tc = mp_parse_track(content);
+ c = tc->text;
+ if(c)
+ {
+#ifdef HAVE_STRTOL
+ errno = 0;
+ j = strtol(c, (char **)NULL, 10);
+ if(errno != ERANGE) v1->track = j;
+ else v1->track = 0;
+#else
+ v1->track = atoi(c);
+#endif
+ }
+ else v1->track = 0;
+ xfree(c);
+ mp_free_text_content(tc);
+ mp_free_content(content);
+
+ content = mp_get_content(tag, MP_GENRE);
+ tc = mp_parse_genre(content);
+ c = tc->text;
+ for(j = 0; c, j < GLL; j++) {
+ if(!strcmp(genre_list[j], c))
+ {
+ v1->genre = j;
+ k = 1;
+ }
+ }
+ if(!c) v1->genre = 0xFF;
+ xfree(c);
+ mp_free_text_content(tc);
+ mp_free_content(content);
+
+ id3v1_truncate_tag(v1);
+
+ id3v2_free_tag(tag->tag);
+
+ tag->version = 1;
+ tag->tag = v1;
+
+ return 0;
+}
+
+int
+mp_is_valid_v1_value(int field, char *value)
+{
+ int len = 30;
+ int j;
+
+ switch(field) {
+ case MP_YEAR:
+ len = 4;
+ break;
+
+ case MP_TRACK:
+#ifdef HAVE_STRTOL
+ errno = 0;
+ j = strtol(value, (char **)NULL, 10);
+ if(errno != ERANGE) return 1;
+ else return 0;
+#else
+ return 1; /* poor fellow */
+#endif
+
+ case MP_GENRE:
+ for(j = 0; j < GLL; j++) {
+ if(!strcmp(genre_list[j], value))
+ {
+ return 1;
+ }
+ return 0;
+ }
+ }
+
+ /* Check string length */
+ if(strlen(value) > len) return 0;
+ else return 1;
+}
+
+
+void
+mp_free_list(id3_tag_list *list)
+{
+ if(!list) return;
+
+ /* free tag */
+ if(list->tag) mp_free_tag(list->tag);
+
+ /* free next element */
+ if(list->next) mp_free_list(list->next);
+
+ /* free this element */
+ xfree(list);
+}
+
+id3_tag*
+mp_alloc_tag(void)
+{
+ /* The tags initialized version makes a different. Generally spoken, we
+ like to make id3v1 tags if possible and therefor set the version to 1
+ here. This matters in mp_set_content(). */
+ return mp_alloc_tag_with_version(1);
+}
+
+id3_tag*
+mp_alloc_tag_with_version(int v)
+{
+ id3_tag* ret;
+
+ if(v != 1 && v != 2) return NULL;
+
+ ret = XMALLOCD(id3_tag, "mp_alloc_tag_with_version:ret");
+ ret->version = v;
+ if(v == 1)
+ {
+ ret->tag = XMALLOCD0(id3v1_tag, "mp_alloc_tag_with_version:ret->tag");
+ ((id3v1_tag*)ret->tag)->genre = 0xFF;
+ }
+ else
+ {
+ id3v2_tag *v2;
+ /* XXX */
+ ret->tag = XMALLOCD0(id3v2_tag, "mp_alloc_tag_with_version:ret->tag");
+ v2 = (id3v2_tag*)ret->tag;
+ v2->header = XMALLOCD0(id3v2_header, "mp_alloc_tag_with_version:v2->header");
+//if ID3VERSION == "2.4"
+ v2->header->version_minor = 4;
+//else
+ v2->header->version_minor = 3;
+//endif
+ v2->header->version_revision = 0;
+ v2->header->unsyncronization = 1;
+ v2->header->has_extended_header = 0;
+ v2->header->is_experimental = 1;
+ v2->header->has_footer = 0;
+ v2->header->flags = 0;
+ v2->header->total_tag_size = 0;
+ v2->header->extended_header = NULL;
+ v2->frame_list = NULL;
+ }
+ return ret;
+}
+
+void
+mp_free_tag(id3_tag *tag)
+{
+ if(!tag) return;
+
+ if(tag->version == 1)
+ {
+ id3v1_free_tag(tag->tag);
+ }
+ else if(tag->version == 2)
+ {
+ id3v2_free_tag(tag->tag);
+ }
+ xfree(tag);
+}
+
+void
+mp_free_content(id3_content *content)
+{
+ if(!content) return;
+ xfree(content->data);
+ xfree(content);
+}
+
+void
+mp_free_text_content(id3_text_content *content)
+{
+ if(!content) return;
+ xfree(content->text);
+ xfree(content);
+}
+
+void
+mp_free_comment_content(id3_comment_content *content)
+{
+ if(!content) return;
+ xfree(content->language);
+ xfree(content->short_descr);
+ xfree(content->text);
+ xfree(content);
+}
+
+
diff --git a/libmpio/mplib/mplib.h b/libmpio/mplib/mplib.h
new file mode 100644
index 0000000..8b3fe64
--- /dev/null
+++ b/libmpio/mplib/mplib.h
@@ -0,0 +1,438 @@
+/*
+ * mplib - a library that enables you to edit ID3 tags
+ * Copyright (C) 2001,2002 Stefan Podkowinski
+ *
+ * This 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; version 2.1.
+ *
+ * This 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __MPLIB_H
+#define __MPLIB_H
+
+
+/* __BEGIN_DECLS should be used at the beginning of your declarations,
+ so that C++ compilers don't mangle their names. Use __END_DECLS at
+ the end of C declarations. */
+#undef __BEGIN_DECLS
+#undef __END_DECLS
+#ifdef __cplusplus
+# define __BEGIN_DECLS extern "C" {
+# define __END_DECLS }
+#else
+# define __BEGIN_DECLS /* empty */
+# define __END_DECLS /* empty */
+#endif
+
+/* __P is a macro used to wrap function prototypes, so that compilers
+ that don't understand ANSI C prototypes still work, and ANSI C
+ compilers can issue warnings about type mismatches. */
+#undef __P
+#if defined (__STDC__) || defined (_AIX) \
+ || (defined (__mips) && defined (_SYSTYPE_SVR4)) \
+ || defined(WIN32) || defined(__cplusplus)
+# define __P(protos) protos
+#else
+# define __P(protos) ()
+#endif
+
+
+__BEGIN_DECLS
+
+
+
+/*************************************/
+/* Defines */
+/*************************************/
+
+#define MP_ARTIST 1
+#define MP_TITLE 2
+#define MP_ALBUM 3
+#define MP_GENRE 4
+#define MP_COMMENT 5
+#define MP_YEAR 6
+#define MP_TRACK 7
+
+#define ISO_8859_1 0
+#define UTF16 1
+#define UTF16BE 2
+#define UTF8 3
+
+
+/*************************************/
+/* errno values */
+/*************************************/
+#define MP_EERROR 1
+#define MP_EFNF 2
+#define MP_EFCOMPR 3
+#define MP_EFENCR 4
+/*define MP_EUNICODE 5*/
+#define MP_EVERSION 6
+
+
+/*************************************/
+/* Structs and company */
+/*************************************/
+
+/* Header structure with 4 segments containing 32 bit header information */
+typedef struct _mpeg_header
+{
+ unsigned int syncword; /* Sync Word */
+ unsigned int version; /* Version number */
+ unsigned int layer; /* Layer number */
+ unsigned int protbit; /* Protection Bit */
+ unsigned int bitrate; /* kbit/sec */
+ unsigned int samplingfreq; /* hz */
+ unsigned int padbit; /* Padding bit */
+ unsigned int privbit; /* Private Bit */
+ unsigned int mode; /* Stereo, Joint-Stereo, Dual-Channel, Mono */
+ unsigned int mode_ext; /* Mode extension */
+ unsigned int copyright; /* Copyright yes/no */
+ unsigned int originalhome; /* Original datastream yes/no */
+ unsigned int emphasis; /* Emphasis bits */
+} mpeg_header;
+
+
+/* Generic tag structure */
+typedef struct _id3_tag
+{
+ int version; /* tags version, either 1 or 2 or -1 if not supported */
+ void *tag; /* pointer to specific struct */
+} id3_tag;
+
+
+/* list of tags found in file */
+typedef struct _id3_tag_list
+{
+ id3_tag *tag;
+ struct _id3_tag_list *next;
+ struct _id3_tag_list *first;
+} id3_tag_list;
+
+
+/*
+ * The following structures are ment as low-level data holders. I strongly
+ * suggest you to use the appropriate generic functions below to access them.
+ */
+
+/* V 1 */
+
+/* ID3v1 tag structure */
+typedef struct _id3v1_tag
+{
+ char *title;
+ char *artist;
+ char *album;
+ char *year;
+ char *comment;
+ unsigned char track; /* track binary encoded */
+ unsigned char genre; /* index on genre list - 0xFF for null */
+} id3v1_tag;
+
+
+/* V 2 */
+
+/* ID3v2 Frame structure */
+typedef struct _id3v2_frame
+{
+ char* frame_id; /* The frame id e.g. TALB */
+ unsigned char status_flag;
+ unsigned char format_flag;
+ char *data;
+ unsigned int data_size; /* frame size excluding header, incl. enc.,lang.,etc.
+ (total frame size - 10) */
+} id3v2_frame;
+
+/* single linked list referencing a number of frames */
+typedef struct _id3v2_frame_list
+{
+ struct _id3v2_frame *data;
+ struct _id3v2_frame_list *next;
+ struct _id3v2_frame_list *start;
+} id3v2_frame_list;
+
+/* ID3v2 Extended Header structure */
+typedef struct _id3v2_extended_header
+{
+ unsigned long size;
+ char *flag_bytes;
+ unsigned int no_flag_bytes;
+ unsigned int is_update;
+ unsigned int crc_data_present;
+ unsigned char crc_data_length;
+ unsigned char* crc_data;
+ unsigned int restrictions;
+ unsigned char restrictions_data_length;
+ unsigned char* restrictions_data;
+} id3v2_extended_header;
+
+/* ID3v2 Header structure */
+typedef struct _id3v2_header
+{
+ /* Version 2.minor.revision */
+ unsigned int version_minor;
+ unsigned int version_revision;
+ char flags; /* Flags - should only be set by mplib and does only contain
+ the following infos */
+ unsigned int unsyncronization;
+ unsigned int has_extended_header;
+ unsigned int is_experimental;
+ unsigned int has_footer;
+ unsigned long total_tag_size; /* is size of all tag elements including
+ header and footer (each 10 bytes) */
+ id3v2_extended_header *extended_header; /* Extended header */
+} id3v2_header;
+
+
+/* ID3v2 tag structure */
+typedef struct _id3v2_tag
+{
+ id3v2_header *header;
+ id3v2_frame_list *frame_list;
+} id3v2_tag;
+
+/* A fields content unparsed */
+typedef struct _id3_content
+{
+ unsigned int compressed;
+ unsigned int encrypted;
+ char *data;
+ unsigned int length;
+} id3_content;
+
+typedef enum _id3_encoding
+{
+ iso_8859_1 = ISO_8859_1,
+ utf16 = UTF16,
+ utf16be = UTF16BE,
+ utf8 = UTF8
+} id3_encoding;
+
+typedef struct _id3_text_content
+{
+ id3_encoding encoding;
+ char *text; /* Null terminated text */
+} id3_text_content;
+
+typedef struct _id3_comment_content
+{
+ id3_encoding encoding;
+ char *language; /* ISO Language code */
+ char *short_descr; /* Null term. content short description */
+ char *text; /* Null terminated text */
+} id3_comment_content;
+
+/***************************************/
+/* Functions */
+/***************************************/
+
+/* Allocates a MPEG header structure from a file
+ * Arg 1 - The filename
+ * Returns - A pointer to a new initialized header structure - NULL on IO Error
+ */
+extern mpeg_header *mp_get_mpeg_header_from_file __P((const char*));
+
+
+/* Gets the header structure from a file descriptor
+ * Arg 1 - The file descriptor
+ * Returns - A pointer to a new initialized header structure - NULL on IO Error
+ */
+extern mpeg_header *mp_get_mpeg_header_from_fd __P((int));
+
+
+/* Frees a mpeg header structure
+ * Arg 1 - The allocated mpeg header
+ */
+#define mp_free_mpeg_header(str) xfree(str)
+
+
+/* Allocates a label with the appropriate header field value as a string */
+extern char *mp_get_str_version __P((const mpeg_header*));
+extern char *mp_get_str_layer __P((const mpeg_header*));
+extern char *mp_get_str_bitrate __P((const mpeg_header*));
+extern char *mp_get_str_samplingfreq __P((const mpeg_header*));
+extern char *mp_get_str_mode __P((const mpeg_header*));
+
+
+/* Allocates and fills a list of tags found in the given file. This list
+ * will contain at least one and at most two tags or is NULL if no tags
+ * have been found.
+ * Arg 1 - The files name/file descriptor to search for tags
+ * Returns - A pointer to a initialized list struct or null if no tags have
+ * been found
+ */
+extern id3_tag_list* mp_get_tag_list_from_file __P((const char*));
+extern id3_tag_list* mp_get_tag_list_from_fd __P((int));
+
+
+/* Frees a tag list beginning with the given element XXX */
+extern void mp_free_list __P((id3_tag_list*));
+
+
+/* Gets the first content found of a specified field in the given tag and
+ * allocates a struct.
+ * Arg 1 - The tag
+ * Arg 2 - The fields identifier
+ *
+ * Returns The new allocated content for the specified field or NULL
+ * On NULL: errno set to the following values
+ * MP_EERROR - General failure: may occure on wrong usage, but should never happen
+ * MP_EFNF - Field does not exists in tag /invalid identifier
+ * MP_EVERSION - Tag has a version set that is not supported by the library
+ */
+extern id3_content* mp_get_content __P((const id3_tag*, int));
+
+/* It's posible that a tag has multiple ocurances of a field.
+ * Use this function to get a specified field by position. The first
+ * ocurance of the field in the tag is 0.
+ * e.g.: To get the third comment in an id3v2 tag use
+ * mp_get_content_at_pos(tag, MP_COMMENT, 2);
+ * Arg 1 - The tag
+ * Arg 2 - The fields identifier
+ * Arg 3 - The content position in the tag
+ * Returns - see mp_get_content
+ */
+extern id3_content* mp_get_content_at_pos __P((const id3_tag*, int, int));
+
+/* Gets a custom fields content and allocates a struct. This function can
+ * only be applied to ID3v2 tags. It will lookup a by the given identifier
+ * and return its content.
+ * Arg 1 - The tag
+ * Arg 2 - The field names identifier e.g. ENCR
+ * Returns - see mp_get_content
+ */
+extern id3_content* mp_get_content_custom __P((const id3_tag*, const char*));
+
+/* See mp_get_content_at_pos() and mp_get_content_custom()
+ * Arg 1 - The tag
+ * Arg 2 - The field names identifier e.g. ENCR
+ * Arg 3 - The content position in the tag
+ * Returns - see mp_get_content
+ */
+extern id3_content* mp_get_content_custom_at_pos __P((const id3_tag*, const char*, int));
+
+/* Frees a content struct */
+extern void mp_free_content __P((id3_content*));
+extern void mp_free_text_content __P((id3_text_content*));
+extern void mp_free_comment_content __P((id3_comment_content*));
+
+
+/* Copys the value of a specified field into the given tag. The content
+ * argument may be freed after using this function. The way a content
+ * is represented in a tag depends from the tags version and kind of field.
+ * I.e. it may be nessecary to represent a track number as a binary value in a v1
+ * tag or to embeded it into a frame for a v2 tag. The caller just needs to
+ * give the correct identifier with the value as a id3_content and to take
+ * care of freeing the id3_content value afterwards.
+ * Arg 1 - The tag to edit
+ * Arg 2 - The fields identifier
+ * Arg 3 - The fields new content
+ * Returns - 0 success or one of the following errors
+ * MP_EERROR - General failure: may occure on wrong usage, but should never happen
+ * MP_EFNF - Field does not exists in tag /invalid identifier
+ * MP_EVERSION - Function isn't able to handle a tag of this version
+ */
+extern int mp_set_content __P((id3_tag*, int, id3_content*));
+extern int mp_set_content_at_pos __P((id3_tag*, int, id3_content*, int));
+
+/* Sets up a new custom field with the given value
+ * Arg 1 - The tag to edit
+ * Arg 2 - The new fields name - A four chars upper case identifier e.g. ENCR
+ * Arg 3 - The fields new content
+ * Returns - See mp_set_content
+ */
+extern int mp_set_custom_content __P((id3_tag*, char*, id3_content*));
+extern int mp_set_custom_content_at_pos __P((id3_tag*, char*, id3_content*, int));
+
+/* Writes the tag to the specified file
+ * Arg 1 - The tag list to be added to file - may be NULL for deleting all tags
+ * Arg 2 - The files name/file descriptor
+ * Returns - 0 on success or one of the following errors
+ * MP_EERROR - General failure: may occure on wrong usage, but should never happen
+ * MP_EVERSION - Function isn't able to handle a tag of this version
+ */
+extern int mp_write_to_file __P((const id3_tag_list*, const char*));
+extern int mp_write_to_fd __P((const id3_tag_list*, int));
+
+/* Deletes all tags in file
+ * Arg 1 - The filename of fd
+ * Return - 0 on success
+ */
+extern int mp_del_tags_from_file __P((const char*));
+extern int mp_del_tags_from_fd __P((int));
+
+/* Deletes all tags in file with the specified version
+ * Arg 1 - The filename or fd
+ * Arg 2 - The version
+ */
+extern int mp_del_tags_by_ver_from_file __P((const char*, int));
+extern int mp_del_tags_by_ver_from_fd __P((int, int));
+
+/* Converts a tag to id3v1 or id3v2 tag format
+ * Arg 1 - The tag to be converted
+ * Returns - 0 on success or one of the following errors
+ * MP_EVERSION - Function isn't able to handle a tag of this version
+ */
+extern int mp_convert_to_v1 __P((id3_tag*));
+extern int mp_convert_to_v2 __P((id3_tag*));
+
+/* Checks wether the given value would be a valid v1 field
+ * Arg 1 - The field
+ * Arg 2 - The value
+ * Returns - 0 if test failed
+ */
+extern int mp_is_valid_v1_value __P((int, char*));
+
+/* Parses a content field
+ * Arg 1 - the content to parse
+ * Returns - A pointer to a new initialized structure suitable for the content
+ * or NULL
+ * On NULL: errno set to the following values
+ * MP_EERROR - General failure: may occure on wrong usage, but should never happen
+ * MP_EFENCR - The value for this field has been encrypted and can thus not be retrieved
+ * MP_EFCOMPR - The value for this field has been compressed and can thus not be retrieved
+ */
+extern id3_text_content *mp_parse_artist __P((const id3_content*));
+extern id3_text_content *mp_parse_title __P((const id3_content*));
+extern id3_text_content *mp_parse_album __P((const id3_content*));
+extern id3_text_content *mp_parse_year __P((const id3_content*));
+extern id3_text_content *mp_parse_genre __P((const id3_content*));
+extern id3_text_content *mp_parse_track __P((const id3_content*));
+extern id3_text_content *mp_parse_text __P((const id3_content*));
+extern id3_comment_content *mp_parse_comment __P((const id3_content*));
+
+/* Assembles content from a comont text content
+ * Arg 1 - the text
+ * Arg 2 - the texts encoding (NULL)
+ * Returns - A pointer to a new initialized content structure
+ */
+extern id3_content *mp_assemble_text_content __P((const char*, id3_encoding));
+
+/* Assembles content from a comment
+ * Arg 1 - the text
+ * Arg 2 - a short describtion to the text (NULL)
+ * Arg 3 - the texts encoding
+ * Arg 4 - the comments language (NULL)
+ * Returns - A pointer to a new initialized content structure
+ */
+extern id3_content *mp_assemble_comment_content __P((const char*, const char*, id3_encoding, const char*));
+
+/* Gets a new allocated tag */
+extern id3_tag* mp_alloc_tag __P((void));
+extern id3_tag* mp_alloc_tag_with_version __P((int));
+
+/* Frees tag struct */
+extern void mp_free_tag __P((id3_tag *));
+
+__END_DECLS
+
+#endif /* __MPLIB_H */
diff --git a/libmpio/mplib/mplib_paas.c b/libmpio/mplib/mplib_paas.c
new file mode 100644
index 0000000..441e474
--- /dev/null
+++ b/libmpio/mplib/mplib_paas.c
@@ -0,0 +1,256 @@
+/*
+ * mplib - a library that enables you to edit ID3 tags
+ * Copyright (C) 2001,2002 Stefan Podkowinski
+ *
+ * This 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; version 2.1.
+ *
+ * This 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 this library; 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
+
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#elif HAVE_STRINGS_H
+# include <strings.h>
+#endif /*STDC_HEADERS*/
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+# include <sys/types.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+#include "mplib.h"
+#include "xmalloc.h"
+
+
+
+/*******************************************************************************************
+ * Parse functions
+ *******************************************************************************************/
+
+ id3_text_content*
+ mp_parse_artist(const id3_content* content)
+ {
+ return mp_parse_text(content);
+ }
+
+ id3_text_content*
+ mp_parse_title(const id3_content* content)
+ {
+ return mp_parse_text(content);
+ }
+
+ id3_text_content*
+ mp_parse_album(const id3_content* content)
+ {
+ return mp_parse_text(content);
+ }
+
+ id3_text_content*
+ mp_parse_year(const id3_content* content)
+ {
+ return mp_parse_text(content);
+ }
+
+ id3_text_content*
+ mp_parse_genre(const id3_content* content)
+ {
+ return mp_parse_text(content);
+ }
+
+ id3_text_content*
+ mp_parse_track(const id3_content* content)
+ {
+ return mp_parse_text(content);
+ }
+
+ id3_comment_content*
+ mp_parse_comment(const id3_content* content)
+ {
+ id3_comment_content* cc;
+ int i, e;
+
+ if(!content || !content->data)
+ {
+ errno = MP_EERROR;
+ return NULL;
+ }
+
+ if(content->encrypted)
+ {
+ errno = MP_EFENCR;
+ return NULL;
+ }
+ if(content->compressed)
+ {
+ errno = MP_EFCOMPR;
+ return NULL;
+ }
+
+ cc = XMALLOCD0(id3_comment_content, "mp_parse_comment:cc");
+
+ e = content->data[0];
+ if(e >= ISO_8859_1 && e <= UTF8) cc->encoding = e;
+ else cc->encoding = 0;
+
+ cc->language = xmallocd(4, "mp_parse_comment:cc->language");
+ cc->language[0] = content->data[1];
+ cc->language[1] = content->data[2];
+ cc->language[2] = content->data[3];
+ cc->language[3] = 0;
+
+ if(content->data[4]) /* short descr. */
+ {
+ i = strlen(content->data + 4) + 1;
+ cc->short_descr = xmallocd(i, "mp_parse_comment:cc->short_descr");
+ strncpy(cc->short_descr, content->data + 4, i);
+ }
+ else
+ {
+ cc->short_descr = NULL;
+ i = 1;
+ }
+
+ cc->text = xmallocd(content->length - 4 - i + 1, "mp_parse_comment:cc->text");
+ memcpy(cc->text, content->data + 4 + i, content->length - 4 - i);
+ cc->text[content->length - 4 - i] = 0;
+
+ return cc;
+ }
+
+ id3_text_content*
+ mp_parse_text(const id3_content* content)
+ {
+ id3_text_content* tc;
+ int e;
+
+ if(!content || !content->data)
+ {
+ errno = MP_EERROR;
+ return NULL;
+ }
+
+ if(content->encrypted)
+ {
+ errno = MP_EFENCR;
+ return NULL;
+ }
+ if(content->compressed)
+ {
+ errno = MP_EFCOMPR;
+ return NULL;
+ }
+
+ tc = XMALLOCD0(id3_text_content, "mp_parse_text:tc");
+ tc->text = xmallocd(content->length, "mp_parse_text:tc->text");
+ e = content->data[0];
+ if(e >= ISO_8859_1 && e <= UTF8) tc->encoding = e;
+ else tc->encoding = 0;
+
+ memcpy(tc->text, content->data + 1, content->length - 1);
+ tc->text[content->length - 1] = 0;
+ /* XXX multiple entries */
+ return tc;
+ }
+
+ /*******************************************************************************************
+ * Assemble functions
+ *******************************************************************************************/
+
+ id3_content*
+ mp_assemble_artist_content(const char* text, id3_encoding enc)
+ {
+ return mp_assemble_text_content(text, enc);
+ }
+
+ id3_content*
+ mp_assemble_title_content(const char* text, id3_encoding enc)
+ {
+ return mp_assemble_text_content(text, enc);
+ }
+
+ id3_content*
+ mp_assemble_album_content(const char* text, id3_encoding enc)
+ {
+ return mp_assemble_text_content(text, enc);
+ }
+
+ id3_content*
+ mp_assemble_year_content(const char* text, id3_encoding enc)
+ {
+ return mp_assemble_text_content(text, enc);
+ }
+
+ id3_content*
+ mp_assemble_genre_content(const char* text, id3_encoding enc)
+ {
+ return mp_assemble_text_content(text, enc);
+ }
+
+ id3_content*
+ mp_assemble_text_content(const char* text, id3_encoding enc)
+ {
+ id3_content *ret;
+
+ if(!text) return NULL;
+
+ ret = XMALLOCD0(id3_content, "mp_assemble_text_content:ret");
+ ret->length = strlen(text) + 1;
+ ret->data = xmallocd(ret->length, "mp_asseble_text_content:ret->data");
+ ret->data[0] = enc;
+ strncpy(ret->data + 1, text, strlen(text));
+
+ return ret;
+ }
+
+ id3_content*
+ mp_assemble_comment_content(const char* text, const char* short_descr, id3_encoding enc, const char* lang)
+ {
+ id3_content *ret;
+
+ if(!text) return NULL;
+
+ ret = XMALLOCD0(id3_content, "mp_assemble_comment_content:ret");
+ ret->length = strlen(text) + 5;
+ if(short_descr) ret->length += strlen(short_descr);
+
+ ret->data = xmallocd(ret->length, "mp_assemble_comment_content:ret->data");
+ ret->data[0] = enc;
+ if(lang && strlen(lang) == 3)
+ {
+ ret->data[1] = lang[0];
+ ret->data[2] = lang[1];
+ ret->data[3] = lang[2];
+ }
+ else
+ {
+ ret->data[1] = 'X';
+ ret->data[2] = 'X';
+ ret->data[3] = 'X';
+ }
+ if(short_descr) strcpy(ret->data + 4, short_descr);
+ else ret->data[4] = 0;
+
+ if(short_descr) strncpy(ret->data + 5 + strlen(short_descr), text, strlen(text));
+ else strncpy(ret->data + 5, text, strlen(text));
+
+ return ret;
+
+ }
diff --git a/libmpio/mplib/mplib_s.c b/libmpio/mplib/mplib_s.c
new file mode 100644
index 0000000..2c336b4
--- /dev/null
+++ b/libmpio/mplib/mplib_s.c
@@ -0,0 +1,1177 @@
+/*
+ * mplib - a library that enables you to edit ID3 tags
+ * Copyright (C) 2001,2002 Stefan Podkowinski
+ *
+ * This 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; version 2.1.
+ *
+ * This 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 this library; 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
+
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#elif HAVE_STRINGS_H
+# include <strings.h>
+#endif /*STDC_HEADERS*/
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+# include <sys/types.h>
+#endif
+
+
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/stat.h>
+
+#include "mplib_s.h"
+#include "xmalloc.h"
+
+
+
+/******************************/
+/* Static functions */
+/******************************/
+
+static id3v1_tag*
+id3v1_get_tag(int fd)
+{
+ id3v1_tag *tag;
+ char *c;
+
+ tag = XMALLOCD0(id3v1_tag, "id3v1_get_tag:tag");
+
+ c = (char *)xmallocd(3, "id3v1_get_tag:c");
+
+ if(lseek(fd, -128L, SEEK_END) == -1) goto exit_on_error;
+ if(read(fd, c, 3) < 3) goto exit_on_error;
+ if(strncmp(c, "TAG", 3) != 0) goto exit_on_error;
+
+ tag->title = (char *)xmallocd(31, "id3v1_get_tag:tag->title");
+ if(read(fd, tag->title, 30) < 30) goto exit_on_error;
+ if(tag->title[0] == 0 || id3_is_only_space(tag->title, 30)) {
+ xfree(tag->title);
+ tag->title = NULL;
+ } else tag->title[30] = 0;
+
+ tag->artist = (char*)xmallocd(31, "id3v1_get_tag:tag->artist");
+ if(read(fd, tag->artist, 30) < 30) goto exit_on_error;
+ if(tag->artist[0] == 0 || id3_is_only_space(tag->artist, 30)) {
+ xfree(tag->artist);
+ tag->artist = NULL;
+ } else tag->artist[30] = 0;
+
+ tag->album = (char*)xmallocd(31, "id3v1_get_tag:tag->album");
+ if(read(fd, tag->album, 30) < 30) goto exit_on_error;
+ if(tag->album[0] == 0 || id3_is_only_space(tag->album, 30)) {
+ xfree(tag->album);
+ tag->album = NULL;
+ } else tag->album[30] = 0;
+
+ tag->year = (char*)xmallocd(5, "id3v1_get_tag:tag->year");
+ if(read(fd, tag->year, 4) < 4) goto exit_on_error;
+ if(tag->year[0] == 0 || id3_is_only_space(tag->year, 4)) {
+ xfree(tag->year);
+ tag->year = NULL;
+ } else tag->year[4] = 0;
+
+ tag->comment = (char*)xmallocd(31, "id3v1_get_tag:tag->comment");
+ if(read(fd, tag->comment, 30) < 30) goto exit_on_error;
+ tag->comment[30] = 0;
+
+ if(read(fd, &(tag->genre), 1) < 1) goto exit_on_error;
+
+ /* Looking for v1.1 track info */
+ if(tag->comment && tag->comment[28] == 0 && tag->comment[29] != 0)
+ {
+ tag->track = tag->comment[29];
+ tag->comment[29] = 0;
+ }
+ else
+ {
+ tag->track = 0;
+ }
+
+ /* Set comment to NULL if not set - this happens at this point because */
+ /* there maybe a track info anyway */
+ if(tag->comment[0] == 0 || id3_is_only_space(tag->comment, 28)) {
+ xfree(tag->comment);
+ tag->comment = NULL;
+ }
+
+ xfree(c);
+ return tag;
+
+ exit_on_error:
+
+ xfree(c);
+ id3v1_free_tag(tag);
+ return NULL;
+}
+
+static int
+id3v1_add_tag(int fd, id3v1_tag *tag)
+{
+ int i, j;
+ void *blank, *set;
+ char *b_tag, *b_tag_start;
+
+ blank = xmallocd0(30, "id3v1_add_tag:blank");
+ set = xmallocd(30, "id3v1_add_tag:set");
+ memset(set, 0xFF, 30);
+ b_tag = b_tag_start = (char *)xmallocd0(128, "id3v1_add_tag:b_tag");
+
+ strncpy(b_tag, "TAG", 3); b_tag += 3;
+
+ if(tag->title)
+ {
+ j = strlen(tag->title);
+ strncpy(b_tag, tag->title, j); b_tag += j;
+ i = 30 - j;
+ if(i > 0)
+ {
+ strncpy(b_tag, blank, i); b_tag += i;
+ }
+ }
+ else
+ {
+ strncpy(b_tag, blank, 30); b_tag += 30;
+ }
+
+ if(tag->artist)
+ {
+ j = strlen(tag->artist);
+ strncpy(b_tag, tag->artist, j); b_tag += j;
+ i = 30 - j;
+ if(i > 0)
+ {
+ strncpy(b_tag, blank, i); b_tag += i;
+ }
+ }
+ else
+ {
+ strncpy(b_tag, blank, 30); b_tag += 30;
+ }
+
+ if(tag->album)
+ {
+ j = strlen(tag->album);
+ strncpy(b_tag, tag->album, j); b_tag += j;
+ i = 30 - j;
+ if(i > 0)
+ {
+ strncpy(b_tag, blank, i); b_tag += i;
+ }
+ }
+ else
+ {
+ strncpy(b_tag, blank, 30); b_tag += 30;
+ }
+
+ if(tag->year)
+ {
+ j = strlen(tag->year);
+ strncpy(b_tag, tag->year, j); b_tag += j;
+ i = 4 - j;
+ if(i > 0)
+ {
+ strncpy(b_tag, blank, i); b_tag += i;
+ }
+ }
+ else
+ {
+ strncpy(b_tag, blank, 4); b_tag += 4;
+ }
+
+ if(tag->comment)
+ {
+ int hastrack = 0;
+ j = strlen(tag->comment);
+ if(tag->track > 0) hastrack = 1;
+ if(hastrack && j > 28)
+ {
+ strncpy(b_tag, tag->comment, 28); b_tag += 28;
+ }
+ else
+ {
+ strncpy(b_tag, tag->comment, j); b_tag += j;
+ i = ((tag->track > 0) ? 28 : 30) - j;
+ }
+ if(i > 0)
+ {
+ strncpy(b_tag, blank, i); b_tag += i;
+ }
+ }
+ else
+ {
+ strncpy(b_tag, blank, (tag->track > 0) ? 28 : 30);
+ b_tag += (tag->track > 0) ? 28 : 30;
+ }
+
+ if(tag->track > 0)
+ {
+ strncpy(b_tag, blank, 1); b_tag += 1;
+ strncpy(b_tag, &(tag->track), 1); b_tag += 1;
+ }
+ if(tag->genre != 0xFF)
+ {
+ strncpy(b_tag, &(tag->genre), 1); b_tag += 1;
+ }
+ else
+ {
+ strncpy(b_tag, set, 1); b_tag += 1;
+ }
+
+ j = 0;
+
+ if(lseek(fd, 0L, SEEK_END) != -1)
+ {
+ if(write(fd, b_tag - 128, 128) < 128) j = 1;
+ }
+ else j = 1;
+
+ xfree(b_tag_start);
+ xfree(blank);
+ xfree(set);
+
+ return j;
+}
+
+static int
+id3v2_add_tag(int fd, id3v2_tag *tag, id3v2_tag *old)
+{
+ unsigned char *btag, *btag_start;
+ unsigned char flag = 0;
+ int i, j;
+ char *b_tag, *b_tag_start, *d;
+ id3v2_frame_list *frame_list;
+ id3v2_frame *frame;
+
+ /* at first we are going to write the tags raw data into
+ the btag byte array */
+ btag = btag_start = xmallocd0(tag->header->total_tag_size,
+ "id3v2_add_tag:btag");
+ strncpy(btag, "ID3", 3); btag += 3;
+ *btag = (char)tag->header->version_minor; btag += 1;
+ *btag = (char)tag->header->version_revision; btag += 1;
+ flag |= ((tag->header->unsyncronization & 1) << 7);
+ flag |= ((tag->header->has_extended_header & 1) << 6);
+ flag |= ((tag->header->is_experimental & 1) << 5);
+ flag |= ((tag->header->has_footer & 1) << 4);
+ memcpy(btag, &flag, 1); btag += 1;
+
+ if(old)
+ {
+ i = old->header->total_tag_size - 10;
+ if(old->header->has_footer) i -= 10;
+ }
+ else
+ {
+ i = tag->header->total_tag_size - 10;
+ if(tag->header->has_footer) i -= 10;
+ /* add padding to total size we mean to store on disk */
+ /* mplib does not use any kind of padding internaly */
+/* i += 1024; */ /* no padding needed for MPIO use */
+ }
+
+ d = id3_sync32(i);
+ btag[0] = d[0];
+ btag[1] = d[1];
+ btag[2] = d[2];
+ btag[3] = d[3];
+ xfree(d);
+ btag += 4;
+
+ if(tag->header->has_extended_header)
+ {
+ d = id3_sync32(tag->header->extended_header->size);
+ btag[0] = d[0];
+ btag[1] = d[1];
+ btag[2] = d[2];
+ btag[3] = d[3];
+ xfree(d);
+ btag += 4;
+
+ *btag = (char)tag->header->extended_header->no_flag_bytes; btag += 1;
+ flag = ((tag->header->extended_header->is_update & 1) << 6);
+ flag |= ((tag->header->extended_header->crc_data_present & 1) << 5);
+ flag |= ((tag->header->extended_header->restrictions & 1) << 4);
+ memcpy(btag, &flag, 1); btag += 1;
+ if(tag->header->extended_header->is_update)
+ {
+ btag[0] = 0; btag += 1;
+ }
+ if(tag->header->extended_header->crc_data_present)
+ {
+ int length = tag->header->extended_header->crc_data_length ? tag->header->extended_header->crc_data_length : 5;
+ *btag = (char)length; btag += 1;
+ memcpy(btag, tag->header->extended_header->crc_data, length); btag += 1;
+ }
+ if(tag->header->extended_header->restrictions)
+ {
+ int length = tag->header->extended_header->restrictions_data_length ? tag->header->extended_header->restrictions_data_length : 5;
+ *btag = (char)length; btag += 1;
+ memcpy(btag, tag->header->extended_header->restrictions_data, length); btag += 1;
+ }
+ }
+
+ frame_list = tag->frame_list;
+ while(frame_list) {
+ int j;
+ frame = frame_list->data;
+
+ strncpy(btag, frame->frame_id, 4); btag += 4;
+ d = id3_sync32(frame->data_size);
+ btag[0] = d[0];
+ btag[1] = d[1];
+ btag[2] = d[2];
+ btag[3] = d[3];
+ xfree(d);
+ btag += 4;
+ memcpy(btag, &frame->status_flag, 1); btag += 1;
+ memcpy(btag, &frame->format_flag, 1); btag += 1;
+
+ memcpy(btag, frame->data, frame->data_size); btag += frame->data_size;
+
+ frame_list = frame_list->next;
+ }
+
+ /* XXX footer not supported yet */
+
+ /* if an old tag was provided it is desired to overwrite it */
+ /* else this is a brand new tag */
+ if(old) {
+ FILE *file;
+ void *ptr = xmallocd0(old->header->total_tag_size - tag->header->total_tag_size, "id3v2_add_tag:ptr");
+ if(!(file = fdopen(fd, "r+b")))
+ {
+ xfree(ptr);
+ goto exit_on_error;
+ }
+
+ fseek(file, 0, SEEK_SET);
+ if(fwrite(btag_start, tag->header->total_tag_size, 1, file) < 1)
+ {
+ xfree(ptr);
+ goto exit_on_error;
+ }
+
+ /* write padding till end of old tag */
+ if(fwrite(ptr, old->header->total_tag_size - tag->header->total_tag_size, 1, file) < 1) {
+ xfree(ptr);
+ goto exit_on_error;
+ }
+
+ fflush(file);
+ xfree(ptr);
+
+ } else {
+ FILE *file, *tmp;
+ int read;
+ void *ptr, *blank;
+ unsigned char *c;
+
+ ptr = xmallocd(4096, "id3v2_add_tag:ptr");
+ blank = xmallocd0(1024, "id3v2_add_tag:blank");
+
+ file = fdopen(fd, "r+b");
+ tmp = tmpfile();
+ if(!(file && tmp))
+ {
+ fflush(file);
+ fclose(tmp);
+ xfree(ptr);
+ xfree(blank);
+ goto exit_on_error;
+ }
+
+ fseek(file, 0, SEEK_SET);
+ fseek(tmp, 0, SEEK_SET);
+
+ /* write tag in tmp file */
+ fwrite(btag_start, tag->header->total_tag_size, 1, tmp);
+
+ /* Write 1024b padding */
+ /* no padding needed for MPIO use */
+/* fwrite(blank, 1024, 1, tmp); */
+
+ /* write rest of file */
+ while(!feof(file))
+ {
+ read = fread(ptr, 1, 4096, file);
+ if(fwrite(ptr, 1, read, tmp) != read && !feof(file))
+ {
+ fflush(file);
+ fclose(tmp);
+ xfree(ptr);
+ xfree(blank);
+ goto exit_on_error;
+ }
+ }
+
+ fflush(tmp);
+
+ fseek(file, 0, SEEK_SET);
+ fseek(tmp, 0, SEEK_SET);
+ while(!feof(tmp))
+ {
+ read = fread(ptr, 1, 4096, tmp);
+ if(fwrite(ptr, 1, read, file) != read && !feof(tmp))
+ {
+ fflush(file);
+ fclose(tmp);
+ xfree(ptr);
+ xfree(blank);
+ goto exit_on_error;
+ }
+ }
+
+ fflush(file);
+ fclose(tmp);
+
+ xfree(ptr);
+ xfree(blank);
+ }
+
+ xfree(btag_start);
+ return 0;
+
+ exit_on_error:
+ xfree(btag_start);
+ return MP_EERROR;
+}
+
+static int
+id3v1_del_tag(int fd)
+{
+ int nlength;
+ unsigned char *c;
+ struct stat fs;
+
+ if(fstat(fd, &fs)) return 1;
+
+ if(fs.st_size < 128) return 1; /* Hardly a valid mpeg file.. */
+
+ c = (char *)xmallocd(3, "id3v1_del_tag:c");
+ if(lseek(fd, -128L, SEEK_END) == -1) goto exit_on_error;
+ if(read(fd, c, 3) < 3) goto exit_on_error;
+ if(strncmp(c, "TAG", 3)) goto exit_on_error;
+ xfree(c);
+
+ nlength = fs.st_size - 128;
+
+ if(ftruncate(fd, nlength)) return 1;
+
+ return 0;
+
+ exit_on_error:
+ xfree(c);
+ return 1;
+}
+
+
+static int
+id3v2_del_tag(int fd, id3v2_tag *t)
+{
+ unsigned char *c;
+ long tag_len, file_len;
+ FILE *file, *tmp;
+ int read;
+ void *ptr;
+ id3v2_tag *tfound = NULL;;
+
+ if(!t)
+ {
+ t = id3v2_get_tag(fd);
+ if(!t) return 0;
+ else tfound = t;
+ }
+
+ ptr = xmallocd(4096, "id3v2_del_tag:ptr");
+
+ tag_len = t->header->total_tag_size;
+ file_len = lseek(fd, 0, SEEK_END);
+ if(file_len < 1 || tag_len < 1) goto exit_on_error;
+
+ /* use os system buffering */
+ file = fdopen(fd, "r+b");
+ tmp = tmpfile();
+ if(!(file && tmp)) goto exit_on_error;
+
+ fseek(file, tag_len, SEEK_SET);
+ fseek(tmp, 0, SEEK_SET);
+ while(!feof(file))
+ {
+ read = fread(ptr, 1, 4096, file);
+ if(fwrite(ptr, 1, read, tmp) != read && !feof(file))
+ goto exit_on_error;
+ }
+
+ fflush(tmp);
+
+ fseek(file, 0, SEEK_SET);
+ fseek(tmp, 0, SEEK_SET);
+ while(!feof(tmp))
+ {
+ read = fread(ptr, 1, 4096, tmp);
+ if(fwrite(ptr, 1, read, file) != read && !feof(tmp))
+ goto exit_on_error;
+ }
+
+ fclose(tmp);
+ xfree(ptr);
+ if(tfound) id3v2_free_tag(tfound);
+ return 0;
+
+ exit_on_error:
+ fclose(tmp);
+ xfree(ptr);
+ if(tfound) id3v2_free_tag(tfound);
+ return 1;
+}
+
+
+static int
+id3v1_truncate_tag(id3v1_tag *tag)
+{
+ int notrunc = 0;
+ int len = 0;
+ void *ptr;
+
+ if(tag->title && (len = strlen(tag->title)) > 30)
+ {
+ realloc(tag->title, 31);
+ tag->title[30] = 0;
+ }
+
+ if(tag->artist && (len = strlen(tag->artist)) > 30)
+ {
+ realloc(tag->artist, 31);
+ tag->artist[30] = 0;
+ }
+
+ if(tag->album && (len = strlen(tag->album)) > 30)
+ {
+ realloc(tag->album, 31);
+ tag->album[30] = 0;
+ }
+
+ if(tag->year && (len = strlen(tag->year)) > 4)
+ {
+ realloc(tag->title, 5);
+ tag->title[4] = 0;
+ }
+
+ if(tag->comment)
+ {
+ int max = (tag->track > 0) ? 28 : 30;
+ if((len = strlen(tag->comment)) > max)
+ {
+ realloc(tag->comment, max + 1);
+ tag->comment[max] = 0;
+ }
+ }
+
+ return notrunc;
+}
+
+static int
+id3_is_only_space(char *str, int strlen)
+{
+ int i = 0;
+
+ while(i < strlen)
+ {
+ if(str[i] != 0x20) return 0;
+ i++;
+ }
+
+ return 1;
+}
+
+static id3v2_tag*
+id3v2_get_tag(int fd)
+{
+ unsigned char *c;
+ id3v2_header *header;
+ id3v2_frame_list *frame_list;
+ id3v2_frame *frame;
+ id3v2_tag *tag = NULL;
+ int i;
+
+ if(lseek(fd, 0L, SEEK_SET) == -1) return NULL;
+
+ c = (unsigned char*)xmallocd0(1024, "id3v2_get_tag:c");
+
+ if(read(fd, c, 10) < 10) goto exit_on_error;
+
+ c[10] = 0;
+
+ if(strncmp(c, "ID3", 3)) goto exit_on_error;
+
+ header = XMALLOCD0(id3v2_header, "id3v2_get_tag:header");
+ header->version_minor = c[3];
+ header->version_revision = c[4];
+ header->flags = c[5];
+ header->unsyncronization = (c[5] & 128) >> 7;
+ header->has_extended_header = (c[5] & 64) >> 6;
+ header->is_experimental = (c[5] & 32) >> 5;
+ header->has_footer = (c[5] & 16) >> 4;
+
+ header->total_tag_size = id3_unsync32(c, 6) + 10;
+ if(header->has_footer) header->total_tag_size += 10;
+
+ tag = XMALLOCD0(id3v2_tag, "id3v2_get_tag:tag");
+
+ /* check if version is supported */
+ if(c[3] != 3 && c[3] != 4)
+ {
+ xfree(c);
+ tag->header = header;
+ tag->frame_list = NULL;
+ return tag;
+ }
+
+ frame_list = XMALLOCD0(id3v2_frame_list, "id3v2_get_tag:frame_list");
+ frame_list->start = frame_list;
+
+ /* assigning header and frame list to tag */
+ tag->header = header;
+ tag->frame_list = frame_list;
+
+ if(header->has_extended_header)
+ {
+ id3v2_extended_header *xt_header = XMALLOCD0(id3v2_extended_header,
+ "id3v2_get_tag:id3v2_extended_header");
+
+ header->extended_header = xt_header;
+
+ read(fd, c, 4); /* get length of extended header */
+ xt_header->size = id3_unsync32(c, 0);
+
+ read(fd, c, 1); /* get number of flags */
+ xt_header->no_flag_bytes = (c[0] > 0) ? c[0] : 1;
+
+ read(fd, c, xt_header->no_flag_bytes); /* get flag bytes */
+ xt_header->is_update = (c[0] & 64) >> 6;
+ xt_header->crc_data_present = (c[0] & 32) >> 5;
+ xt_header->restrictions = (c[0] & 16) >> 4;
+
+ /* Flag data */
+ if(xt_header->is_update) read(fd, c, 1); /* Data length ind. is 0 -skip */
+ if(xt_header->crc_data_present) {
+ read(fd, c, 1); /* data length - shoud be 5 */
+ if(*c != 5) goto exit_on_error; /* else things might
+ break badly */
+ xt_header->crc_data_length = *c;
+ xt_header->crc_data = xmallocd0(*c, "id3v2_get_tag:xt_header->crc_data");
+ read(fd, xt_header->crc_data, *c);
+ }
+ if(xt_header->restrictions) {
+ read(fd, c, 1); /* data length - shoud be 1 */
+ if(*c != 1) goto exit_on_error;
+ xt_header->restrictions_data_length = *c;
+ xt_header->restrictions_data = xmallocd0(*c,
+ "id3v2_get_tag:xt_header->restrictions_data");
+ read(fd, xt_header->restrictions_data, *c);
+ }
+ }
+
+ /* Read frames */
+ while(lseek(fd, 0L, SEEK_CUR) < header->total_tag_size)
+ {
+ int hasEnc = 0, hasLang = 0, d;
+
+ read(fd, c, 10); /* Read header */
+
+ /* break if padding is reached - this should never happen here.. */
+ if(c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0) break;
+
+ /* Check if possible id is alpha numeric */
+ if(!isalnum(c[0]) || !isalnum(c[1]) || !isalnum(c[2]) || !isalnum(c[3])) break;
+
+ frame = XMALLOCD(id3v2_frame, "id3v2_get_tag:frame");
+ frame->frame_id = xmallocd(4, "id3v2_get_tag:frame->frame_id");
+ strncpy(frame->frame_id, c, 4);
+ frame->data_size = id3_unsync32(c, 4);
+ frame->status_flag = c[8];
+ frame->format_flag = c[9];
+
+ /* Getting frame content */
+ frame->data = xmallocd(frame->data_size, "id3v2_get_tag:frame->data_size");
+ read(fd, frame->data, frame->data_size);
+
+ /* Add frame to list */
+ if(frame_list->data)
+ {
+ frame_list->next = XMALLOCD(id3v2_frame_list, "id3v2_get_tag:frame_list->next");
+ frame_list->next->start = frame_list->start;
+ frame_list = frame_list->next;
+ frame_list->next = NULL;
+ }
+ frame_list->data = frame;
+ }
+
+ xfree(c);
+ return tag;
+
+ exit_on_error:
+
+ xfree(c);
+ id3v2_free_tag(tag);
+ return NULL;
+}
+
+static char **
+id3v2_get_names(id3v2_tag *tag)
+{
+ id3v2_frame *frame;
+ id3v2_frame_list *frame_list;
+ char **clist;
+ int i;
+
+ if(!tag->frame_list) return NULL;
+
+ frame_list = tag->frame_list;
+
+ i = id3_get_no_frames(tag);
+ clist = xmallocd(sizeof(char*) * i+1, "id3v2_get_names:clist");
+ clist[i] = 0;
+
+ for(i = 0; frame_list; i++)
+ {
+ if(!frame_list->data) continue;
+ frame = frame_list->data;
+
+ if(!frame->frame_id) continue;
+ clist[i] = xmallocd(5, "id3v2_get_names:clist[i]");
+ strncpy(clist[i], frame->frame_id, 4);
+ clist[i][4] = 0;
+ frame_list = frame_list->next;
+ }
+ return clist;
+}
+
+
+static id3_content*
+id3v1_get_content(id3v1_tag *tag, int field)
+{
+ int i;
+ char *c;
+ id3_content *ret;
+
+ switch(field)
+ {
+ case MP_ARTIST:
+ if(!tag->artist)
+ {
+ errno = MP_EFNF;
+ return NULL;
+ }
+ return mp_assemble_text_content(tag->artist, ISO_8859_1);
+
+ case MP_TITLE:
+ if(!tag->title)
+ {
+ errno = MP_EFNF;
+ return NULL;
+ }
+ return mp_assemble_text_content(tag->title, ISO_8859_1);
+
+ case MP_ALBUM:
+ if(!tag->album)
+ {
+ errno = MP_EFNF;
+ return NULL;
+ }
+ return mp_assemble_text_content(tag->album, ISO_8859_1);
+
+ case MP_YEAR:
+ if(!tag->year)
+ {
+ errno = MP_EFNF;
+ return NULL;
+ }
+ return mp_assemble_text_content(tag->year, ISO_8859_1);
+
+ case MP_COMMENT:
+ if(!tag->comment)
+ {
+ errno = MP_EFNF;
+ return NULL;
+ }
+ return mp_assemble_comment_content(tag->comment, NULL, ISO_8859_1, NULL);
+
+ case MP_GENRE:
+ if(tag->genre < GLL)
+ {
+ return mp_assemble_text_content(genre_list[tag->genre], ISO_8859_1);
+ }
+ else
+ {
+ errno = MP_EFNF;
+ return NULL;
+ }
+
+ case MP_TRACK:
+ if(!tag->track)
+ {
+ errno = MP_EFNF;
+ return NULL;
+ }
+
+ if(tag->track < 10) i = 2;
+ else if(tag->track < 100) i = 3;
+ else i = 4;
+ c = xmallocd(i, "id3v1_get_content:c");
+ snprintf(c, i, "%d", tag->track);
+ ret = mp_assemble_text_content(c, ISO_8859_1);
+ xfree(c);
+ return ret;
+
+ default:
+ errno = MP_EFNF;
+ return NULL;
+ }
+}
+
+static id3_content*
+id3v2_get_content_at_pos(id3v2_tag *tag, const char *field, int pos)
+{
+ id3v2_frame_list *frame_list;
+ id3v2_frame *frame;
+ int i, found, j;
+
+ if(!tag->frame_list || !field)
+ {
+ errno = MP_EERROR;
+ return NULL;
+ }
+
+ frame_list = tag->frame_list;
+
+ for(i = 0, found = 0; frame_list; i++, frame_list = frame_list->next)
+ {
+ if(!frame_list->data) continue;
+ frame = frame_list->data;
+
+ if(!frame->frame_id || !frame->data) continue;
+ if(strncmp(frame->frame_id, field, 4)) continue;
+
+ if(found == pos)
+ {
+ id3_content *ret = XMALLOCD0(id3_content,
+ "id3v2_get_content_at_pos:ret");
+ if(frame_is_compressed(frame)) ret->compressed = 1;
+ else ret->compressed = 0;
+
+ if(frame_is_encrypted(frame)) ret->encrypted = 1;
+ else ret->encrypted = 0;
+
+ ret->length = frame->data_size;
+ ret->data = xmallocd(frame->data_size, "id3v2_get_content_at_pos:ret->data");
+ ret->data = memcpy(ret->data, frame->data, frame->data_size);
+
+ return ret;
+ }
+
+ found++;
+ }
+
+ errno = MP_EFNF;
+ return NULL;
+}
+
+static long
+id3_sync(unsigned char* data, long length)
+{
+ int i;
+
+ for(i = 0; i < length - 1; i++)
+ {
+ if(((data[i] & 0xFF) == 0xFF && (data[i+1] & 0xE0) == 0xE0) ||
+ (i+2 < length && (data[i] & 0xFF) == 0xFF && data[i+1] == 0 && data[i+2] != 0))
+ {
+
+ realloc(data, length + 1); length++;
+ memmove(data + i+2, data + i+1, length - i - 2);
+ memset(data + i+1, 0, 1);
+ }
+ }
+
+ return length;
+}
+
+static long
+id3_unsync(unsigned char* data, long length)
+{
+ /* TODO */
+ /* this function is supposed to make syncsafe values normal again */
+ /* we don't need this yet, because there are no fields supported that will */
+ /* have the unsynchronization scheme applied */
+
+
+}
+
+static unsigned int
+id3_unsync32(unsigned char* c, int start)
+{
+ return c[start+3] + (c[start+2] << 7) + (c[start+1] << 14) + (c[start] << 21);
+}
+
+int
+id3_get_no_frames(id3v2_tag *tag)
+{
+ int i;
+ id3v2_frame_list *frame_list = tag->frame_list;
+
+ for(i = 0; frame_list; i++)
+ frame_list = frame_list->next;
+
+ return i;
+}
+
+static unsigned char *
+id3_sync32(unsigned int i)
+{
+ unsigned char *c = (unsigned char *)xmallocd0(4, "id3_sync32:c");
+
+ c[3] = (i & 0x7f);
+ c[2] = ((i & 0x80) >> 7) | (((i & 0x7f00) >> 8) << 1);
+ c[1] = ((i & 0x8000) >> 15) | (((i & 0x7f0000) >> 16) << 1);
+ c[0] = ((i & 0x800000) >> 23) | (((i & 0x7f000000) >> 24) << 1);
+
+ return c;
+}
+
+id3v2_frame*
+id3_lookup_frame(id3v2_frame_list *list, const char *field, const int pos)
+{
+ int cur = 0;
+
+ if(!list || !field) return NULL;
+
+ do
+ {
+ if(!strcmp(list->data->frame_id, field))
+ {
+ if(cur == pos) return list->data;
+ cur++;
+ }
+ } while((list = list->next));
+
+ return NULL;
+}
+
+int
+id3_remove_frame(id3v2_frame_list *list, id3v2_frame *frame)
+{
+ if(!list || !frame) return MP_EERROR;
+
+ /* Frame is first element in list */
+ if(list->data == frame)
+ {
+ xfree(list->data);
+ list->next->start = list->next;
+ xfree(list);
+ return 0;
+ }
+
+ /* Iterate through others */
+ do
+ {
+ if(list->next->data == frame)
+ {
+ list->next = list->next->next;
+ xfree(frame);
+ return 0;
+ }
+ } while((list = list->next));
+
+ return 1;
+}
+
+
+int
+id3_add_frame(id3v2_frame_list *list, char *field, char *new_value, int len)
+{
+ id3v2_frame *frame;
+ char *new_valuecp;
+ long len_sync;
+
+ if(!list || !new_value || !field || strlen(field) != 4) return MP_EERROR;
+
+ // make a copy of the given value to include it in the frame
+ new_valuecp = xmallocd(len,
+ "id3_add_frame:new_valuecp");
+ memcpy(new_valuecp, new_value, len);
+ new_value = new_valuecp;
+
+ /* make sync safe */
+/* len_sync = id3_sync(new_value, len); */
+ len_sync = len; /* disabled sync for MPIO use */
+
+ frame = XMALLOCD(id3v2_frame, "id3_add_frame:frame");
+ frame->frame_id = xmallocd(4, "id3_add_frame:frame->frame_id");
+ strncpy(frame->frame_id, field, 4);
+ frame->data = new_value;
+ frame->status_flag = 0;
+
+ if(len != len_sync) frame->format_flag = 64;
+ else frame->format_flag = 0;
+ frame->data_size = len_sync;
+
+ /* Empty list */
+ if(!list->data)
+ {
+ list->data = frame;
+ return 0;
+ }
+
+ /* iterate to last element */
+ while(list->next) list = list->next;
+
+ list->next = XMALLOCD(id3v2_frame_list, "id3_add_frame:list->next");
+ list->next->start = list->start;
+ list = list->next;
+ list->next = NULL;
+ list->data = frame;
+
+ return 0;
+}
+
+
+#define BUF_SIZE 4096
+
+static int
+id3_lseek_syncword(int fd)
+{
+ unsigned char *data = (unsigned char*) xmallocd(BUF_SIZE, "id3_lseek_syncword:data");
+ int ret;
+
+ // Reset the reading offset of the fd
+ lseek(fd, SEEK_SET, 0);
+
+ if(read(fd, data, BUF_SIZE) < 1)
+ {
+ xfree(data);
+ return 0; /* return false on EOF */
+ }
+
+ ret = id3_lseek_syncword_r(fd, data, 0);
+ xfree(data);
+ return ret;
+}
+
+static int
+id3_lseek_syncword_r(int fd, unsigned char *data, int checked)
+{
+ unsigned char lastchar;
+ int i;
+
+ for(i = 0; i + 1 < BUF_SIZE; i++)
+ {
+ if(((data[i] & 0xFF)== 0xFF) && ((data[i+1] & 0xE0)== 0xE0))
+ {
+ lseek(fd, checked + i, SEEK_SET);
+ return 0;
+ }
+ }
+
+ lastchar = data[BUF_SIZE - 1];
+
+ if(read(fd, data, BUF_SIZE) < 1) return 0; /* return false on EOF */
+
+ if(((lastchar & 0xFF)== 0xFF) && ((data[0] & 0xE0)== 0xE0))
+ {
+ lseek(fd, checked + BUF_SIZE - 1, SEEK_SET);
+ return 0;
+ }
+
+ return id3_lseek_syncword_r(fd, data, checked + BUF_SIZE);
+}
+
+static id3_tag*
+id3v1_alloc_tag(void)
+{
+ id3_tag *tag = XMALLOCD(id3_tag, "id3v1_alloc_tag:tag");
+ id3v1_tag *v1 = XMALLOCD0(id3v1_tag, "id3v1_alloc_tag:v1");
+
+ tag->tag = v1;
+ tag->version = 1;
+
+ v1->genre = 0xFF;
+
+ return tag;
+}
+
+static id3_tag*
+id3v2_alloc_tag(void)
+{
+ id3_tag *tag = XMALLOCD(id3_tag, "id3v2_alloc_tag:tag");
+ id3v2_tag *v2 = XMALLOCD0(id3v2_tag, "id3v2_alloc_tag:v2");
+
+ tag->tag = v2;
+ tag->version = 2;
+
+ return tag;
+}
+
+static void
+id3v1_free_tag(id3v1_tag* v1)
+{
+ xfree(v1->artist);
+ xfree(v1->album);
+ xfree(v1->title);
+ xfree(v1->year);
+ xfree(v1->comment);
+ xfree(v1);
+}
+
+static void
+id3v2_free_tag(id3v2_tag* v2)
+{
+ id3v2_frame_list* doomed;
+ id3v2_frame *frame;
+
+ if(!v2) return;
+
+ xfree(v2->header->extended_header);
+ xfree(v2->header);
+
+ if(!v2->frame_list)
+ {
+ xfree(v2);
+ return;
+ }
+
+ /* Freeing frames */
+ do
+ {
+ frame = (id3v2_frame*)v2->frame_list->data;
+ if(frame->frame_id) xfree(frame->frame_id);
+ if(frame->data) xfree(frame->data);
+ xfree(v2->frame_list->data);
+
+ doomed = v2->frame_list->next;
+ xfree(v2->frame_list);
+
+ } while((v2->frame_list = doomed));
+
+ xfree(v2);
+}
diff --git a/libmpio/mplib/mplib_s.h b/libmpio/mplib/mplib_s.h
new file mode 100644
index 0000000..7803a5e
--- /dev/null
+++ b/libmpio/mplib/mplib_s.h
@@ -0,0 +1,153 @@
+/*
+ * mplib - a library that enables you to edit ID3 tags
+ * Copyright (C) 2001,2002 Stefan Podkowinski
+ *
+ * This 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; version 2.1.
+ *
+ * This 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "mplib.h"
+
+#ifndef __MPLIB_S_H
+#define __MPLIB_S_H
+
+#undef __BEGIN_DECLS
+#undef __END_DECLS
+#ifdef __cplusplus
+# define __BEGIN_DECLS extern "C" {
+# define __END_DECLS }
+#else
+# define __BEGIN_DECLS /* empty */
+# define __END_DECLS /* empty */
+#endif
+
+
+__BEGIN_DECLS
+
+
+/************************************/
+/* Members */
+/************************************/
+
+/* Bitrates br_[version]_[layer] ) */
+const static int br_1_1[16] = { -1, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 };
+const static int br_1_2[16] = { -1, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 };
+const static int br_1_3[16] = { -1, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 };
+const static int br_2_1[16] = { -1, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 };
+const static int br_2_2[16] = { -1, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 };
+const static int br_2_3[16] = { -1, 8, 16, 24, 32, 64, 80, 56, 64, 128, 160, 112, 128, 256, 320, 0 };
+
+#define GLL 148
+const static char *genre_list[GLL] =
+{ "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz",
+ "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno",
+ "Industrial", "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno",
+ "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental",
+ "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "Alternative Rock", "Bass", "Soul", "Punk",
+ "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", "Darkwave",
+ "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy",
+ "Cult", "Gangsta Rap", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret",
+ "New Wave", "Psychedelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk",
+ "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock",
+ "National Folk", "Swing", "Fast-Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass",
+ "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock",
+ "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson", "Opera",
+ "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", "Porn Groove", "Satire", "Slow Jam",
+ "Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad", "Rythmic Soul", "Freestyle",
+ "Duet", "Punk Rock", "Drum Solo", "A Cappella", "Euro-House", "Dance Hall", "Goa", "Drum & Bass",
+ "Club-House", "Hardcore", "Terror", "Indie", "BritPop", "Negerpunk", "Polsk Punk", "Beat",
+ "Christian Gangsta Rap", "Heavy Metal", "Black Metal", "Crossover", "Contemporary Christian",
+ "Christian Rock", "Merengue", "Salsa", "Trash Metal", "Anime", "JPop", "Synthpop" };
+
+
+/************************************/
+/* Macros */
+/************************************/
+
+#define frame_is_read_only(id3v2_frame__) ((id3v2_frame__->status_flag >> 4) & 1)
+#define frame_preserve_on_tag_altered(id3v2_frame__) ((id3v2_frame__->status_flag >> 6) & 1)
+#define frame_preserve_on_file_altered(id3v2_frame__) ((id3v2_frame__->status_flag >> 5) & 1)
+
+#define frame_has_group_information(id3v2_frame__) ((id3v2_frame__->format_flag >> 6) & 1)
+#define frame_is_compressed(id3v2_frame__) ((id3v2_frame__->format_flag >> 3) & 1)
+#define frame_is_encrypted(id3v2_frame__) ((id3v2_frame__->format_flag >> 2) & 1)
+#define frame_is_unsynchronised(id3v2_frame__) ((id3v2_frame__->format_flag >> 1) & 1)
+#define frame_has_data_length_indicator(id3v2_frame__) (id3v2_frame__->format_flag & 1)
+
+
+/************************************/
+/* Static functions */
+/************************************/
+
+
+/* Gets the v1/v2 tag from a file */
+static id3v1_tag *id3v1_get_tag(int);
+static id3v2_tag *id3v2_get_tag(int);
+
+/* Adds v1/v2 Tag to file */
+static int id3v1_add_tag(int, id3v1_tag*);
+static int id3v2_add_tag(int, id3v2_tag*, id3v2_tag*);
+
+/* Removes v1/v2 Tag from file */
+static int id3v1_del_tag(int);
+static int id3v2_del_tag(int, id3v2_tag*);
+
+/* Truncates the fields of the tag to the proper lengths */
+static int id3v1_truncate_tag(id3v1_tag*);
+
+/* Returns 1 or 0 whether arg 1 is just filled up with space (0x20) or not */
+static int id3_is_only_space(char*, int);
+
+/* Gets all frame names available in the tag */
+static char **id3v2_get_names(id3v2_tag*);
+
+/* Gets the content of the given field in the tag on the specified position. */
+static id3_content* id3v2_get_content_at_pos(id3v2_tag*, const char*, int);
+static id3_content* id3v1_get_content(id3v1_tag*, int);
+
+/* Gets the number of available frames */
+static int id3_get_no_frames(id3v2_tag*);
+
+/* Returns string of the frames content */
+/* static char* extract_frame_data(char *, int); */
+
+/* Removes a frame from the frame list */
+static int id3_remove_frame(id3v2_frame_list *, id3v2_frame *);
+
+/* Adds a new frame to the list */
+static int id3_add_frame(id3v2_frame_list *, char *, char *, int);
+
+/* Lookups a frame by his identifier on the given position */
+static id3v2_frame* id3_lookup_frame(id3v2_frame_list *, const char *, const int);
+
+/* Sync functions */
+static long id3_unsync(unsigned char*, long);
+static long id3_sync(unsigned char*, long);
+static unsigned int id3_unsync32(unsigned char*, int);
+static unsigned char* id3_sync32(unsigned int);
+
+static int id3_lseek_syncword(int);
+static int id3_lseek_syncword_r(int, unsigned char *, int);
+
+/* Gets a new allocated v1 or v2 tag */
+static id3_tag* id3v1_alloc_tag(void);
+static id3_tag* id3v2_alloc_tag(void);
+
+/* free functions */
+static void id3v1_free_tag(id3v1_tag*);
+static void id3v2_free_tag(id3v2_tag*);
+
+
+__END_DECLS
+
+#endif /* __MPLIB_S_H */
diff --git a/libmpio/mplib/xmalloc.c b/libmpio/mplib/xmalloc.c
new file mode 100644
index 0000000..a66be63
--- /dev/null
+++ b/libmpio/mplib/xmalloc.c
@@ -0,0 +1,167 @@
+/*
+ * mplib - a library that enables you to edit ID3 tags
+ * Copyright (C) 2001,2002 Stefan Podkowinski
+ *
+ * This 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; version 2.1.
+ *
+ * This 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+/* define XMALLOC_CHECK 1 */
+#include "xmalloc.h"
+#ifdef USE_GC
+# include <gc.h>
+# define malloc(str) GC_MALLOC(str)
+#endif
+
+#ifdef XMALLOC_CHECK
+typedef struct _xdelem {
+ void* alloced;
+ void* freed;
+ char* descrl;
+ struct _xdelem* next;
+} xdelem;
+
+xdelem* first = NULL;
+#endif
+
+#define errmsg "mplib: Memory exhausted: Could not allocate %d bytes\n"
+
+void *
+xmalloc(size_t s)
+{
+ return xmallocd(s, NULL);
+}
+
+void *
+xmallocd(size_t s, char* descrl)
+{
+ void *new = (void*)malloc(s);
+#ifdef XMALLOC_CHECK
+ xdelem* cur = (xdelem*)malloc(sizeof(xdelem));
+ cur->next = NULL;
+ cur->freed = NULL;
+ cur->descrl = descrl;
+#endif
+ if(!new) fprintf(stderr, errmsg, s);
+
+#ifdef XMALLOC_CHECK
+ cur->alloced = new;
+ exists:
+ if(!first) first = cur;
+ else {
+ xdelem* last = first;
+ do {
+ if(last->alloced == cur->alloced) {
+ last->freed = NULL;
+ last->descrl = descrl;
+ free(cur);
+ goto exists;
+ }
+ } while(last->next && (last = last->next));
+ last->next = cur;
+ }
+#endif
+
+ return new;
+}
+
+void *
+xmallocd0(size_t s, char* descr)
+{
+#ifdef XMALLOC_CHECK
+ void *new = (void*)xmallocd(s, descr);
+#else
+ void *new = (void*)malloc(s);
+#endif
+ if(!new) fprintf(stderr, errmsg, s);
+ else memset(new, 0, s);
+ return new;
+}
+
+void *
+xmalloc0(size_t s)
+{
+#ifdef XMALLOC_CHECK
+ void *new = (void*)xmalloc(s);
+#else
+ void *new = (void*)malloc(s);
+#endif
+ if(!new) fprintf(stderr, errmsg, s);
+ else memset(new, 0, s);
+ return new;
+}
+
+void *
+xrealloc(void * ptr, size_t s)
+{
+ void *new;
+
+ if(!ptr) return xmalloc(s);
+
+ new = (void*)realloc(ptr, s);
+ if(!new) fprintf(stderr, errmsg, s);
+ return new;
+}
+
+void
+xfree(void* ptr) {
+ if(!ptr) return;
+#ifdef XMALLOC_CHECK
+ if(first) {
+ xdelem* el = first;
+ do {
+ if(el->freed == ptr) {
+ if(el->descrl)
+ printf("XMALLOC: (%s) memory allready freed\n", el->descrl);
+ else
+ printf("XMALLOC: memory allready freed at %h\n", ptr);
+ break;
+ }
+ if(el->alloced == ptr) {
+ el->freed = ptr;
+ break;
+ }
+ } while(el->next && (el = el->next));
+ }
+#endif
+ free(ptr);
+}
+
+
+#ifdef XMALLOC_CHECK
+void
+xprint_malloc_stat(void) {
+ long kb_alloc = 0;
+ long kb_freed = 0;
+ long kb_used = 0;
+ int count_used = 0;
+ xdelem* el = first;
+
+ if(!first) {
+ puts("XMALLOC: No statistic available");
+ }
+ puts("xmalloc statistic:");
+ do {
+ if(!el->freed) {
+ if(el->descrl && !strstr(el->descrl, "ignore"))
+ printf("%s (not freed)\n", el->descrl);
+ else if(!el->descrl) printf("%p (not freed)\n", el->alloced);
+ } else {
+ //if(el->descrl) printf("%s (freed)\n", el->descrl);
+ //else printf("%p (freed)\n", el->alloced);
+ }
+ } while(el->next && (el = el->next));
+}
+#endif
diff --git a/libmpio/mplib/xmalloc.h b/libmpio/mplib/xmalloc.h
new file mode 100644
index 0000000..ecdf1b6
--- /dev/null
+++ b/libmpio/mplib/xmalloc.h
@@ -0,0 +1,58 @@
+/*
+ * mplib - a library that enables you to edit ID3 tags
+ * Copyright (C) 2001,2002 Stefan Podkowinski
+ *
+ * This 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; version 2.1.
+ *
+ * This 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __XMALLOC_H
+#define __XMALLOC_H
+
+/* __BEGIN_DECLS should be used at the beginning of your declarations,
+ so that C++ compilers don't mangle their names. Use __END_DECLS at
+ the end of C declarations. */
+#undef __BEGIN_DECLS
+#undef __END_DECLS
+#ifdef __cplusplus
+# define __BEGIN_DECLS extern "C" {
+# define __END_DECLS }
+#else
+# define __BEGIN_DECLS /* empty */
+# define __END_DECLS /* empty */
+#endif
+
+
+#define XMALLOC(type) ((type *) xmalloc(sizeof(type)))
+#define XMALLOCD(type,descr) ((type *) xmallocd(sizeof(type),descr))
+#define XMALLOC0(type) ((type *) xmalloc0(sizeof(type)))
+#define XMALLOCD0(type,descr) ((type *) xmallocd0(sizeof(type),descr))
+
+__BEGIN_DECLS
+
+/* define XMALLOC_CHECK 1 */
+
+void *xmalloc(size_t);
+void *xmallocd(size_t, char*);
+void *xmalloc0(size_t);
+void *xmallocd0(size_t, char*);
+void *xrealloc(void *, size_t);
+void *xcalloc(size_t, size_t);
+void xfree(void*);
+#ifdef XMALLOC_CHECK
+void xprint_malloc_stat(void);
+#endif
+
+__END_DECLS
+
+#endif /* __XMALLOC_H */