mp3: fix error handling in rewrite_tags()
[sox.git] / src / id3.c
blob9cfb81c47f18c4b0c391e05778869252dee10b15
1 /* libSoX MP3 utilities Copyright (c) 2007-9 SoX contributors
3 * This library is free software; you can redistribute it and/or modify it
4 * under the terms of the GNU Lesser General Public License as published by
5 * the Free Software Foundation; either version 2.1 of the License, or (at
6 * your option) any later version.
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
11 * General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library; if not, write to the Free Software Foundation,
15 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 #include "sox_i.h"
19 #include "id3.h"
21 #ifdef HAVE_ID3TAG
23 #include <id3tag.h>
25 static char const * id3tagmap[][2] =
27 {"TIT2", "Title"},
28 {"TPE1", "Artist"},
29 {"TALB", "Album"},
30 {"TCOM", "Composer"},
31 {"TRCK", "Tracknumber"},
32 {"TDRC", "Year"},
33 {"TCON", "Genre"},
34 {"COMM", "Comment"},
35 {"TPOS", "Discnumber"},
36 {NULL, NULL}
39 static id3_utf8_t * utf8_id3tag_findframe(
40 struct id3_tag * tag, const char * const frameid, unsigned index)
42 struct id3_frame const * frame = id3_tag_findframe(tag, frameid, index);
43 if (frame) {
44 unsigned nfields = frame->nfields;
46 while (nfields--) {
47 union id3_field const *field = id3_frame_field(frame, nfields);
48 int ftype = id3_field_type(field);
49 const id3_ucs4_t *ucs4 = NULL;
50 unsigned nstrings;
52 switch (ftype) {
53 case ID3_FIELD_TYPE_STRING:
54 ucs4 = id3_field_getstring(field);
55 break;
57 case ID3_FIELD_TYPE_STRINGFULL:
58 ucs4 = id3_field_getfullstring(field);
59 break;
61 case ID3_FIELD_TYPE_STRINGLIST:
62 nstrings = id3_field_getnstrings(field);
63 while (nstrings--) {
64 ucs4 = id3_field_getstrings(field, nstrings);
65 if (ucs4)
66 break;
68 break;
71 if (ucs4)
72 return id3_ucs4_utf8duplicate(ucs4); /* Must call free() on this */
75 return NULL;
78 struct tag_info_node
80 struct tag_info_node * next;
81 off_t start;
82 off_t end;
85 struct tag_info {
86 sox_format_t * ft;
87 struct tag_info_node * head;
88 struct id3_tag * tag;
91 static int add_tag(struct tag_info * info)
93 struct tag_info_node * current;
94 off_t start, end;
95 id3_byte_t query[ID3_TAG_QUERYSIZE];
96 id3_byte_t * buffer;
97 long size;
98 int result = 0;
100 /* Ensure we're at the start of a valid tag and get its size. */
101 if (ID3_TAG_QUERYSIZE != lsx_readbuf(info->ft, query, ID3_TAG_QUERYSIZE) ||
102 !(size = id3_tag_query(query, ID3_TAG_QUERYSIZE))) {
103 return 0;
105 if (size < 0) {
106 if (0 != lsx_seeki(info->ft, size, SEEK_CUR) ||
107 ID3_TAG_QUERYSIZE != lsx_readbuf(info->ft, query, ID3_TAG_QUERYSIZE) ||
108 (size = id3_tag_query(query, ID3_TAG_QUERYSIZE)) <= 0) {
109 return 0;
113 /* Don't read a tag more than once. */
114 start = lsx_tell(info->ft);
115 end = start + size;
116 for (current = info->head; current; current = current->next) {
117 if (start == current->start && end == current->end) {
118 return 1;
119 } else if (start < current->end && current->start < end) {
120 return 0;
124 buffer = lsx_malloc((size_t)size);
125 if (!buffer) {
126 return 0;
128 memcpy(buffer, query, ID3_TAG_QUERYSIZE);
129 if ((unsigned long)size - ID3_TAG_QUERYSIZE ==
130 lsx_readbuf(info->ft, buffer + ID3_TAG_QUERYSIZE, (size_t)size - ID3_TAG_QUERYSIZE)) {
131 struct id3_tag * tag = id3_tag_parse(buffer, (size_t)size);
132 if (tag) {
133 current = lsx_malloc(sizeof(struct tag_info_node));
134 if (current) {
135 current->next = info->head;
136 current->start = start;
137 current->end = end;
138 info->head = current;
139 if (info->tag && (info->tag->extendedflags & ID3_TAG_EXTENDEDFLAG_TAGISANUPDATE)) {
140 struct id3_frame * frame;
141 unsigned i;
142 for (i = 0; (frame = id3_tag_findframe(tag, NULL, i)); i++) {
143 id3_tag_attachframe(info->tag, frame);
145 id3_tag_delete(tag);
146 } else {
147 if (info->tag) {
148 id3_tag_delete(info->tag);
150 info->tag = tag;
155 free(buffer);
156 return result;
159 void lsx_id3_read_tag(sox_format_t * ft, sox_bool search)
161 struct tag_info info;
162 id3_utf8_t * utf8;
163 int i;
164 int has_id3v1 = 0;
166 info.ft = ft;
167 info.head = NULL;
168 info.tag = NULL;
171 We look for:
172 ID3v1 at end (EOF - 128).
173 ID3v2 at start.
174 ID3v2 at end (but before ID3v1 from end if there was one).
177 if (search) {
178 if (0 == lsx_seeki(ft, -128, SEEK_END)) {
179 has_id3v1 =
180 add_tag(&info) &&
181 1 == ID3_TAG_VERSION_MAJOR(id3_tag_version(info.tag));
183 if (0 == lsx_seeki(ft, 0, SEEK_SET)) {
184 add_tag(&info);
186 if (0 == lsx_seeki(ft, has_id3v1 ? -138 : -10, SEEK_END)) {
187 add_tag(&info);
189 } else {
190 add_tag(&info);
193 if (info.tag && info.tag->frames) {
194 for (i = 0; id3tagmap[i][0]; ++i) {
195 if ((utf8 = utf8_id3tag_findframe(info.tag, id3tagmap[i][0], 0))) {
196 char * comment = lsx_malloc(strlen(id3tagmap[i][1]) + 1 + strlen((char *)utf8) + 1);
197 sprintf(comment, "%s=%s", id3tagmap[i][1], utf8);
198 sox_append_comment(&ft->oob.comments, comment);
199 free(comment);
200 free(utf8);
203 if ((utf8 = utf8_id3tag_findframe(info.tag, "TLEN", 0))) {
204 unsigned long tlen = strtoul((char *)utf8, NULL, 10);
205 if (tlen > 0 && tlen < ULONG_MAX) {
206 ft->signal.length= tlen; /* In ms; convert to samples later */
207 lsx_debug("got exact duration from ID3 TLEN");
209 free(utf8);
212 while (info.head) {
213 struct tag_info_node * head = info.head;
214 info.head = head->next;
215 free(head);
217 if (info.tag) {
218 id3_tag_delete(info.tag);
222 #else
224 /* Stub for format modules */
225 void lsx_id3_read_tag(sox_format_t *ft, sox_bool search) { }
227 #endif