comparison Plugins/Input/mpg123/id3.c @ 61:fa848bd484d8 trunk

[svn] Move plugins to Plugins/
author nenolod
date Fri, 28 Oct 2005 22:58:11 -0700
parents
children 539a0fa7f030
comparison
equal deleted inserted replaced
60:1771f253e1b2 61:fa848bd484d8
1 /*********************************************************************
2 *
3 * Copyright (C) 1999, 2001, Espen Skoglund
4 * Department of Computer Science, University of Tromsų
5 *
6 * Filename: id3.c
7 * Description: Code for accessing ID3 tags.
8 * Author: Espen Skoglund <espensk@stud.cs.uit.no>
9 * Created at: Fri Feb 5 23:55:13 1999
10 *
11 * $Id: id3.c,v 1.6 2004/07/20 21:47:22 descender Exp $
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 *
27 ********************************************************************/
28 #include "config.h"
29
30 #include <sys/types.h>
31 #include <sys/uio.h>
32 #include <glib.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <stdlib.h>
37 #include <stdarg.h>
38
39 #include "xmms-id3.h"
40 #include "id3_header.h"
41
42
43 /*
44 **
45 ** Functions for accessing the ID3 tag using a memory pointer.
46 **
47 */
48
49 /*
50 * Function id3_seek_mem (id3, offset)
51 *
52 * Seek `offset' bytes forward in the indicated ID3-tag. Return 0
53 * upon success, or -1 if an error occured.
54 *
55 */
56 static int
57 id3_seek_mem(struct id3_tag *id3, int offset)
58 {
59 if (id3->id3_pos + offset > id3->id3_tagsize || id3->id3_pos + offset < 0) {
60 id3_error(id3, "seeking beyond tag boundary");
61 return -1;
62 }
63 id3->s.me.id3_ptr = (char *) id3->s.me.id3_ptr + offset;
64 id3->id3_pos += offset;
65
66 return 0;
67 }
68
69
70 /*
71 * Function id3_read_mem (id3, buf, size)
72 *
73 * Read `size' bytes from indicated ID3-tag. If `buf' is non-NULL,
74 * read into that buffer. Return a pointer to the data which was
75 * read, or NULL upon error.
76 *
77 */
78 static void *
79 id3_read_mem(struct id3_tag *id3, void *buf, int size)
80 {
81 void *ret = id3->s.me.id3_ptr;
82
83 /*
84 * Check boundary.
85 */
86 if (id3->id3_pos + size > id3->id3_tagsize) {
87 return NULL;
88 }
89
90 /*
91 * If buffer is non-NULL, we have to copy the data.
92 */
93 if (buf != NULL) {
94 if (size > ID3_FD_BUFSIZE)
95 return NULL;
96 memcpy(buf, id3->s.me.id3_ptr, size);
97 }
98
99 /*
100 * Update memory pointer.
101 */
102 id3->s.me.id3_ptr = (char *) id3->s.me.id3_ptr + size;
103 id3->id3_pos += size;
104
105 return ret;
106 }
107
108
109 /*
110 **
111 ** Functions for accessing the ID3 tag using a file descriptor.
112 **
113 */
114
115 /*
116 * Function id3_seek_fd (id3, offset)
117 *
118 * Seek `offset' bytes forward in the indicated ID3-tag. Return 0
119 * upon success, or -1 if an error occured.
120 *
121 */
122 static int
123 id3_seek_fd(struct id3_tag *id3, int offset)
124 {
125 /*
126 * Check boundary.
127 */
128 if (id3->id3_pos + offset > id3->id3_tagsize || id3->id3_pos + offset < 0)
129 return -1;
130
131 if (lseek(id3->s.fd.id3_fd, offset, SEEK_CUR) == -1) {
132 id3_error(id3, "seeking beyond tag boundary");
133 return -1;
134 }
135 id3->id3_pos += offset;
136
137 return 0;
138 }
139
140
141 /*
142 * Function id3_read_fd (id3, buf, size)
143 *
144 * Read `size' bytes from indicated ID3-tag. If `buf' is non-NULL,
145 * read into that buffer. Return a pointer to the data which was
146 * read, or NULL upon error.
147 *
148 */
149 static void *
150 id3_read_fd(struct id3_tag *id3, void *buf, int size)
151 {
152 int done = 0;
153
154 /*
155 * Check boundary.
156 */
157 if (id3->id3_pos + size > id3->id3_tagsize) {
158 return NULL;
159 }
160
161 /*
162 * If buffer is NULL, we use the default buffer.
163 */
164 if (buf == NULL) {
165 if (size > ID3_FD_BUFSIZE)
166 return NULL;
167 buf = id3->s.fd.id3_buf;
168 }
169
170 /*
171 * Read until we have slurped as much data as we wanted.
172 */
173 while (done < size) {
174 char *buffer = (char *) buf + done;
175 int ret;
176
177 /*
178 * Try reading from file.
179 */
180 ret = read(id3->s.fd.id3_fd, buffer, size);
181 if (ret <= 0) {
182 id3_error(id3, "read(2) failed");
183 return NULL;
184 }
185
186 id3->id3_pos += ret;
187 done += ret;
188 }
189
190 return buf;
191 }
192
193
194 /*
195 **
196 ** Functions for accessing the ID3 tag using a file pointer.
197 **
198 */
199
200 /*
201 * Function id3_seek_fp (id3, offset)
202 *
203 * Seek `offset' bytes forward in the indicated ID3-tag. Return 0
204 * upon success, or -1 if an error occured.
205 *
206 */
207 static int
208 id3_seek_fp(struct id3_tag *id3, int offset)
209 {
210 /*
211 * Check boundary.
212 */
213 if (id3->id3_pos + offset > id3->id3_tagsize || id3->id3_pos + offset < 0)
214 return -1;
215
216 if (offset > 0) {
217 /*
218 * If offset is positive, we use vfs_fread() instead of vfs_fseek(). This
219 * is more robust with respect to streams.
220 */
221 char buf[64];
222 int r, remain = offset;
223
224 while (remain > 0) {
225 int size = MIN(64, remain);
226 r = vfs_fread(buf, 1, size, id3->s.fp.id3_fp);
227 if (r == 0) {
228 id3_error(id3, "vfs_fread() failed");
229 return -1;
230 }
231 remain -= r;
232 }
233 }
234 else {
235 /*
236 * If offset is negative, we ahve to use vfs_fseek(). Let us hope
237 * that it works.
238 */
239 if (vfs_fseek(id3->s.fp.id3_fp, offset, SEEK_CUR) == -1) {
240 id3_error(id3, "seeking beyond tag boundary");
241 return -1;
242 }
243 }
244 id3->id3_pos += offset;
245
246 return 0;
247 }
248
249
250 /*
251 * Function id3_read_fp (id3, buf, size)
252 *
253 * Read `size' bytes from indicated ID3-tag. If `buf' is non-NULL,
254 * read into that buffer. Return a pointer to the data which was
255 * read, or NULL upon error.
256 *
257 */
258 static void *
259 id3_read_fp(struct id3_tag *id3, void *buf, int size)
260 {
261 int ret;
262
263 /*
264 * Check boundary.
265 */
266 if (id3->id3_pos + size > id3->id3_tagsize) {
267 size = id3->id3_tagsize - id3->id3_pos;
268 }
269
270 /*
271 * If buffer is NULL, we use the default buffer.
272 */
273 if (buf == NULL) {
274 if (size > ID3_FD_BUFSIZE)
275 return NULL;
276 buf = id3->s.fd.id3_buf;
277 }
278
279 /*
280 * Try reading from file.
281 */
282 ret = vfs_fread(buf, 1, size, id3->s.fp.id3_fp);
283 if (ret != size) {
284 id3_error(id3, "vfs_fread() failed");
285 return NULL;
286 }
287
288 id3->id3_pos += ret;
289
290 return buf;
291 }
292
293
294
295
296 /*
297 * Function id3_open_mem (ptr, flags)
298 *
299 * Open an ID3 tag using a memory pointer. Return a pointer to a
300 * structure describing the ID3 tag, or NULL if an error occured.
301 *
302 */
303 struct id3_tag *
304 id3_open_mem(void *ptr, int flags)
305 {
306 struct id3_tag *id3;
307
308 /*
309 * Allocate ID3 structure.
310 */
311 id3 = g_malloc0(sizeof(struct id3_tag));
312
313 /*
314 * Initialize access pointers.
315 */
316 id3->id3_seek = id3_seek_mem;
317 id3->id3_read = id3_read_mem;
318
319 id3->id3_oflags = flags;
320 id3->id3_type = ID3_TYPE_MEM;
321 id3->id3_pos = 0;
322 id3->s.me.id3_ptr = ptr;
323
324 /*
325 * Try reading ID3 tag.
326 */
327 if (id3_read_tag(id3) == -1) {
328 if (~flags & ID3_OPENF_CREATE)
329 goto Return_NULL;
330 id3_init_tag(id3);
331 }
332
333 return id3;
334
335 Return_NULL:
336 g_free(id3);
337 return NULL;
338 }
339
340
341 /*
342 * Function id3_open_fd (fd, flags)
343 *
344 * Open an ID3 tag using a file descriptor. Return a pointer to a
345 * structure describing the ID3 tag, or NULL if an error occured.
346 *
347 */
348 struct id3_tag *
349 id3_open_fd(int fd, int flags)
350 {
351 struct id3_tag *id3;
352
353 /*
354 * Allocate ID3 structure.
355 */
356 id3 = g_malloc0(sizeof(struct id3_tag));
357
358 /*
359 * Initialize access pointers.
360 */
361 id3->id3_seek = id3_seek_fd;
362 id3->id3_read = id3_read_fd;
363
364 id3->id3_oflags = flags;
365 id3->id3_type = ID3_TYPE_FD;
366 id3->id3_pos = 0;
367 id3->s.fd.id3_fd = fd;
368
369 /*
370 * Allocate buffer to hold read data.
371 */
372 id3->s.fd.id3_buf = g_malloc(ID3_FD_BUFSIZE);
373
374 /*
375 * Try reading ID3 tag.
376 */
377 if (id3_read_tag(id3) == -1) {
378 if (~flags & ID3_OPENF_CREATE)
379 goto Return_NULL;
380 id3_init_tag(id3);
381 }
382
383 return id3;
384
385 /*
386 * Cleanup code.
387 */
388 Return_NULL:
389 g_free(id3->s.fd.id3_buf);
390 g_free(id3);
391 return NULL;
392 }
393
394
395 /*
396 * Function id3_open_fp (fp, flags)
397 *
398 * Open an ID3 tag using a file pointer. Return a pointer to a
399 * structure describing the ID3 tag, or NULL if an error occured.
400 *
401 */
402 struct id3_tag *
403 id3_open_fp(VFSFile * fp, int flags)
404 {
405 struct id3_tag *id3;
406
407 /*
408 * Allocate ID3 structure.
409 */
410 id3 = g_malloc0(sizeof(struct id3_tag));
411
412 /*
413 * Initialize access pointers.
414 */
415 id3->id3_seek = id3_seek_fp;
416 id3->id3_read = id3_read_fp;
417
418 id3->id3_oflags = flags;
419 id3->id3_type = ID3_TYPE_FP;
420 id3->id3_pos = 0;
421 id3->s.fp.id3_fp = fp;
422
423 /*
424 * Allocate buffer to hold read data.
425 */
426 id3->s.fp.id3_buf = g_malloc(ID3_FD_BUFSIZE);
427
428 /*
429 * Try reading ID3 tag.
430 */
431 if (id3_read_tag(id3) == -1) {
432 if (~flags & ID3_OPENF_CREATE)
433 goto Return_NULL;
434 id3_init_tag(id3);
435 }
436
437
438 return id3;
439
440 /*
441 * Cleanup code.
442 */
443 Return_NULL:
444 g_free(id3->s.fp.id3_buf);
445 g_free(id3);
446 return NULL;
447 }
448
449
450 /*
451 * Function id3_close (id3)
452 *
453 * Free all resources assoicated with the ID3 tag.
454 *
455 */
456 int
457 id3_close(struct id3_tag *id3)
458 {
459 int ret = 0;
460
461 switch (id3->id3_type) {
462 case ID3_TYPE_MEM:
463 break;
464 case ID3_TYPE_FD:
465 g_free(id3->s.fd.id3_buf);
466 break;
467 case ID3_TYPE_FP:
468 g_free(id3->s.fp.id3_buf);
469 break;
470 case ID3_TYPE_NONE:
471 id3_error(id3, "unknown ID3 type");
472 ret = -1;
473 }
474
475 id3_destroy_frames(id3);
476
477 g_free(id3);
478
479 return ret;
480 }
481
482
483 /*
484 * Function id3_tell (id3)
485 *
486 * Return the current position in ID3 tag. This will always be
487 * directly after the tag.
488 *
489 */
490 #if 0
491 int
492 id3_tell(struct id3_tag *id3)
493 {
494 if (id3->id3_newtag) {
495 return 0;
496 }
497 else {
498 return id3->id3_tagsize + 3 + sizeof(id3_taghdr_t);
499 }
500 }
501 #endif
502
503
504 /*
505 * Function id3_alter_file (id3)
506 *
507 * When altering a file, some ID3 tags should be discarded. As the ID3
508 * library has no means of knowing when a file has been altered
509 * outside of the library, this function must be called manually
510 * whenever the file is altered.
511 *
512 */
513 int
514 id3_alter_file(struct id3_tag *id3)
515 {
516 /*
517 * List of frame classes that should be discarded whenever the
518 * file is altered.
519 */
520 static guint32 discard_list[] = {
521 ID3_ETCO, ID3_EQUA, ID3_MLLT, ID3_POSS, ID3_SYLT,
522 ID3_SYTC, ID3_RVAD, ID3_TENC, ID3_TLEN, ID3_TSIZ,
523 0
524 };
525 struct id3_frame *fr;
526 guint32 id, i = 0;
527
528 /*
529 * Go through list of frame types that should be discarded.
530 */
531 while ((id = discard_list[i++]) != 0) {
532 /*
533 * Discard all frames of that type.
534 */
535 while ((fr = id3_get_frame(id3, id, 1))) {
536 id3_delete_frame(fr);
537 }
538 }
539
540 return 0;
541 }
542
543
544 /*
545 * Function safe_write (fd, buf, size)
546 *
547 * Like write(2), except that the whole buffer will be written.
548 *
549 */
550 static int
551 safe_write(int fd, void *buf, int size)
552 {
553 int remaining = size;
554 char *ptr = buf;
555 int r;
556
557 while (remaining > 0) {
558 if ((r = write(fd, ptr, remaining)) == -1)
559 return -1;
560 remaining -= r;
561 ptr += r;
562 }
563
564 return 0;
565 }
566
567
568 /*
569 * Function id3_write_tag (id3, fd)
570 *
571 * Wrtite the ID3 tag to the indicated file descriptor. Return 0
572 * upon success, or -1 if an error occured.
573 *
574 */
575 int
576 id3_write_tag(struct id3_tag *id3, int fd)
577 {
578 struct id3_frame *fr;
579 GList *node;
580 int size = 0;
581 char buf[ID3_TAGHDR_SIZE];
582
583 /*
584 * Calculate size of ID3 tag.
585 */
586 for (node = id3->id3_frame; node != NULL; node = node->next) {
587 fr = node->data;
588 size += fr->fr_size + ID3_FRAMEHDR_SIZE;
589 }
590
591 /*
592 * Write tag header.
593 */
594 buf[0] = id3->id3_version;
595 buf[1] = id3->id3_revision;
596 buf[2] = id3->id3_flags;
597 ID3_SET_SIZE28(size, buf[3], buf[4], buf[5], buf[6]);
598
599 if (safe_write(fd, "ID3", 3) == -1)
600 return -1;
601 if (safe_write(fd, buf, ID3_TAGHDR_SIZE) == -1)
602 return -1;
603
604 /*
605 * TODO: Write extended header.
606 */
607 #if 0
608 if (id3->id3_flags & ID3_THFLAG_EXT) {
609 id3_exthdr_t exthdr;
610 }
611 #endif
612
613 for (node = id3->id3_frame; node != NULL; node = node->next) {
614 char fhdr[ID3_FRAMEHDR_SIZE];
615
616 fr = node->data;
617
618 /*
619 * TODO: Support compressed headers, encoded
620 * headers, and grouping info.
621 */
622 /* fhdr.fh_id = fr->fr_desc ? g_htonl(fr->fr_desc->fd_id) : 0; */
623 fhdr[3] = (fr->fr_size >> 24) & 0xff;
624 fhdr[4] = (fr->fr_size >> 16) & 0xff;
625 fhdr[5] = (fr->fr_size >> 8) & 0xff;
626 fhdr[6] = fr->fr_size & 0xff;
627 fhdr[7] = (fr->fr_flags >> 8) & 0xff;
628 fhdr[8] = fr->fr_flags & 0xff;
629
630 if (safe_write(fd, fhdr, sizeof(fhdr)) == -1)
631 return -1;
632
633 if (safe_write(fd, fr->fr_data, fr->fr_size) == -1)
634 return -1;
635 }
636 return 0;
637 }