00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 #ifdef HAVE_CONFIG_H
00034 # include "config.h"
00035 #endif
00036
00037 #if STDC_HEADERS
00038 # include <stdlib.h>
00039 # include <string.h>
00040 #elif HAVE_STRINGS_H
00041 # include <strings.h>
00042 #endif
00043
00044 #if HAVE_UNISTD_H
00045 # include <unistd.h>
00046 # include <sys/types.h>
00047 #endif
00048
00049
00050 #include <errno.h>
00051 #include <stdio.h>
00052 #include <fcntl.h>
00053 #include <ctype.h>
00054 #include <sys/stat.h>
00055
00056 #include "mplib_s.h"
00057 #include "xmalloc.h"
00058
00059
00060
00061
00062
00063
00064
00065 static id3v1_tag*
00066 id3v1_get_tag(size_t (*read_func)(void *, size_t, void *),
00067 off_t (*lseek_func)(off_t, int, void *),
00068 void *arg)
00069 {
00070 id3v1_tag *tag;
00071 char *c;
00072
00073 tag = XMALLOCD0(id3v1_tag, "id3v1_get_tag:tag");
00074
00075 c = (char *)xmallocd(3, "id3v1_get_tag:c");
00076
00077 if(lseek_func(-128L, SEEK_END, arg) == -1) goto exit_on_error;
00078 if(read_func(c, 3, arg) < 3) goto exit_on_error;
00079 if(strncmp(c, "TAG", 3) != 0) goto exit_on_error;
00080
00081 tag->title = (char *)xmallocd(31, "id3v1_get_tag:tag->title");
00082 if(read_func(tag->title, 30, arg) < 30) goto exit_on_error;
00083 if(tag->title[0] == 0 || id3_is_only_space(tag->title, 30)) {
00084 xfree(tag->title);
00085 tag->title = NULL;
00086 } else tag->title[30] = 0;
00087
00088 tag->artist = (char*)xmallocd(31, "id3v1_get_tag:tag->artist");
00089 if(read_func(tag->artist, 30, arg) < 30) goto exit_on_error;
00090 if(tag->artist[0] == 0 || id3_is_only_space(tag->artist, 30)) {
00091 xfree(tag->artist);
00092 tag->artist = NULL;
00093 } else tag->artist[30] = 0;
00094
00095 tag->album = (char*)xmallocd(31, "id3v1_get_tag:tag->album");
00096 if(read_func(tag->album, 30, arg) < 30) goto exit_on_error;
00097 if(tag->album[0] == 0 || id3_is_only_space(tag->album, 30)) {
00098 xfree(tag->album);
00099 tag->album = NULL;
00100 } else tag->album[30] = 0;
00101
00102 tag->year = (char*)xmallocd(5, "id3v1_get_tag:tag->year");
00103 if(read_func(tag->year, 4, arg) < 4) goto exit_on_error;
00104 if(tag->year[0] == 0 || id3_is_only_space(tag->year, 4)) {
00105 xfree(tag->year);
00106 tag->year = NULL;
00107 } else tag->year[4] = 0;
00108
00109 tag->comment = (char*)xmallocd(31, "id3v1_get_tag:tag->comment");
00110 if(read_func(tag->comment, 30, arg) < 30) goto exit_on_error;
00111 tag->comment[30] = 0;
00112
00113 if(read_func(&(tag->genre), 1, arg) < 1) goto exit_on_error;
00114
00115
00116 if(tag->comment && tag->comment[28] == 0 && tag->comment[29] != 0)
00117 {
00118 tag->track = tag->comment[29];
00119 tag->comment[29] = 0;
00120 }
00121 else
00122 {
00123 tag->track = 0;
00124 }
00125
00126
00127
00128 if(tag->comment[0] == 0 || id3_is_only_space(tag->comment, 28)) {
00129 xfree(tag->comment);
00130 tag->comment = NULL;
00131 }
00132
00133 xfree(c);
00134 return tag;
00135
00136 exit_on_error:
00137
00138 xfree(c);
00139 id3v1_free_tag(tag);
00140 return NULL;
00141 }
00142
00143 static int
00144 id3v1_add_tag(int fd, id3v1_tag *tag)
00145 {
00146 int i, j;
00147 void *blank, *set;
00148 char *b_tag, *b_tag_start;
00149
00150 blank = xmallocd0(30, "id3v1_add_tag:blank");
00151 set = xmallocd(30, "id3v1_add_tag:set");
00152 memset(set, 0xFF, 30);
00153 b_tag = b_tag_start = (char *)xmallocd0(128, "id3v1_add_tag:b_tag");
00154
00155 strncpy(b_tag, "TAG", 3); b_tag += 3;
00156
00157 if(tag->title)
00158 {
00159 j = strlen(tag->title);
00160 strncpy(b_tag, tag->title, j); b_tag += j;
00161 i = 30 - j;
00162 if(i > 0)
00163 {
00164 strncpy(b_tag, blank, i); b_tag += i;
00165 }
00166 }
00167 else
00168 {
00169 strncpy(b_tag, blank, 30); b_tag += 30;
00170 }
00171
00172 if(tag->artist)
00173 {
00174 j = strlen(tag->artist);
00175 strncpy(b_tag, tag->artist, j); b_tag += j;
00176 i = 30 - j;
00177 if(i > 0)
00178 {
00179 strncpy(b_tag, blank, i); b_tag += i;
00180 }
00181 }
00182 else
00183 {
00184 strncpy(b_tag, blank, 30); b_tag += 30;
00185 }
00186
00187 if(tag->album)
00188 {
00189 j = strlen(tag->album);
00190 strncpy(b_tag, tag->album, j); b_tag += j;
00191 i = 30 - j;
00192 if(i > 0)
00193 {
00194 strncpy(b_tag, blank, i); b_tag += i;
00195 }
00196 }
00197 else
00198 {
00199 strncpy(b_tag, blank, 30); b_tag += 30;
00200 }
00201
00202 if(tag->year)
00203 {
00204 j = strlen(tag->year);
00205 strncpy(b_tag, tag->year, j); b_tag += j;
00206 i = 4 - j;
00207 if(i > 0)
00208 {
00209 strncpy(b_tag, blank, i); b_tag += i;
00210 }
00211 }
00212 else
00213 {
00214 strncpy(b_tag, blank, 4); b_tag += 4;
00215 }
00216
00217 if(tag->comment)
00218 {
00219 int hastrack = 0;
00220 j = strlen(tag->comment);
00221 if(tag->track > 0) hastrack = 1;
00222 if(hastrack && j > 28)
00223 {
00224 strncpy(b_tag, tag->comment, 28); b_tag += 28;
00225 }
00226 else
00227 {
00228 strncpy(b_tag, tag->comment, j); b_tag += j;
00229 i = ((tag->track > 0) ? 28 : 30) - j;
00230 }
00231 if(i > 0)
00232 {
00233 strncpy(b_tag, blank, i); b_tag += i;
00234 }
00235 }
00236 else
00237 {
00238 strncpy(b_tag, blank, (tag->track > 0) ? 28 : 30);
00239 b_tag += (tag->track > 0) ? 28 : 30;
00240 }
00241
00242 if(tag->track > 0)
00243 {
00244 strncpy(b_tag, blank, 1); b_tag += 1;
00245 strncpy(b_tag, &(tag->track), 1); b_tag += 1;
00246 }
00247 if(tag->genre != 0xFF)
00248 {
00249 strncpy(b_tag, &(tag->genre), 1); b_tag += 1;
00250 }
00251 else
00252 {
00253 strncpy(b_tag, set, 1); b_tag += 1;
00254 }
00255
00256 j = 0;
00257
00258 if(lseek(fd, 0L, SEEK_END) != -1)
00259 {
00260 if(write(fd, b_tag - 128, 128) < 128) j = 1;
00261 }
00262 else j = 1;
00263
00264 xfree(b_tag_start);
00265 xfree(blank);
00266 xfree(set);
00267
00268 return j;
00269 }
00270
00271 static int
00272 id3v2_add_tag(int fd, id3v2_tag *tag, id3v2_tag *old)
00273 {
00274 unsigned char *btag, *btag_start;
00275 unsigned char flag = 0;
00276 int i, j;
00277 char *b_tag, *b_tag_start, *d;
00278 id3v2_frame_list *frame_list;
00279 id3v2_frame *frame;
00280
00281
00282
00283 btag = btag_start = xmallocd0(tag->header->total_tag_size,
00284 "id3v2_add_tag:btag");
00285 strncpy(btag, "ID3", 3); btag += 3;
00286 *btag = (char)tag->header->version_minor; btag += 1;
00287 *btag = (char)tag->header->version_revision; btag += 1;
00288 flag |= ((tag->header->unsyncronization & 1) << 7);
00289 flag |= ((tag->header->has_extended_header & 1) << 6);
00290 flag |= ((tag->header->is_experimental & 1) << 5);
00291 flag |= ((tag->header->has_footer & 1) << 4);
00292 memcpy(btag, &flag, 1); btag += 1;
00293
00294 if(old)
00295 {
00296 i = old->header->total_tag_size - 10;
00297 if(old->header->has_footer) i -= 10;
00298 }
00299 else
00300 {
00301 i = tag->header->total_tag_size - 10;
00302 if(tag->header->has_footer) i -= 10;
00303
00304
00305 i += 1024;
00306 }
00307
00308 d = id3_sync32(i);
00309 btag[0] = d[0];
00310 btag[1] = d[1];
00311 btag[2] = d[2];
00312 btag[3] = d[3];
00313 xfree(d);
00314 btag += 4;
00315
00316 if(tag->header->has_extended_header)
00317 {
00318 d = id3_sync32(tag->header->extended_header->size);
00319 btag[0] = d[0];
00320 btag[1] = d[1];
00321 btag[2] = d[2];
00322 btag[3] = d[3];
00323 xfree(d);
00324 btag += 4;
00325
00326 *btag = (char)tag->header->extended_header->no_flag_bytes; btag += 1;
00327 flag = ((tag->header->extended_header->is_update & 1) << 6);
00328 flag |= ((tag->header->extended_header->crc_data_present & 1) << 5);
00329 flag |= ((tag->header->extended_header->restrictions & 1) << 4);
00330 memcpy(btag, &flag, 1); btag += 1;
00331 if(tag->header->extended_header->is_update)
00332 {
00333 btag[0] = 0; btag += 1;
00334 }
00335 if(tag->header->extended_header->crc_data_present)
00336 {
00337 int length = tag->header->extended_header->crc_data_length ? tag->header->extended_header->crc_data_length : 5;
00338 *btag = (char)length; btag += 1;
00339 memcpy(btag, tag->header->extended_header->crc_data, length); btag += 1;
00340 }
00341 if(tag->header->extended_header->restrictions)
00342 {
00343 int length = tag->header->extended_header->restrictions_data_length ? tag->header->extended_header->restrictions_data_length : 5;
00344 *btag = (char)length; btag += 1;
00345 memcpy(btag, tag->header->extended_header->restrictions_data, length); btag += 1;
00346 }
00347 }
00348
00349 frame_list = tag->frame_list;
00350 while(frame_list) {
00351 int j;
00352 frame = frame_list->data;
00353
00354 strncpy(btag, frame->frame_id, 4); btag += 4;
00355 d = id3_sync32(frame->data_size);
00356 btag[0] = d[0];
00357 btag[1] = d[1];
00358 btag[2] = d[2];
00359 btag[3] = d[3];
00360 xfree(d);
00361 btag += 4;
00362 memcpy(btag, &frame->status_flag, 1); btag += 1;
00363 memcpy(btag, &frame->format_flag, 1); btag += 1;
00364
00365 memcpy(btag, frame->data, frame->data_size); btag += frame->data_size;
00366
00367 frame_list = frame_list->next;
00368 }
00369
00370
00371
00372
00373
00374 if(old) {
00375 FILE *file;
00376 void *ptr = xmallocd0(old->header->total_tag_size - tag->header->total_tag_size, "id3v2_add_tag:ptr");
00377 if(!(file = fdopen(fd, "r+b")))
00378 {
00379 xfree(ptr);
00380 goto exit_on_error;
00381 }
00382
00383 fseek(file, 0, SEEK_SET);
00384 if(fwrite(btag_start, tag->header->total_tag_size, 1, file) < 1)
00385 {
00386 xfree(ptr);
00387 goto exit_on_error;
00388 }
00389
00390
00391 if(fwrite(ptr, old->header->total_tag_size - tag->header->total_tag_size, 1, file) < 1) {
00392 xfree(ptr);
00393 goto exit_on_error;
00394 }
00395
00396 fflush(file);
00397 xfree(ptr);
00398
00399 } else {
00400 FILE *file, *tmp;
00401 int read;
00402 void *ptr, *blank;
00403 unsigned char *c;
00404
00405 ptr = xmallocd(4096, "id3v2_add_tag:ptr");
00406 blank = xmallocd0(1024, "id3v2_add_tag:blank");
00407
00408 file = fdopen(fd, "r+b");
00409 tmp = tmpfile();
00410 if(!(file && tmp))
00411 {
00412 fflush(file);
00413 fclose(tmp);
00414 xfree(ptr);
00415 xfree(blank);
00416 goto exit_on_error;
00417 }
00418
00419 fseek(file, 0, SEEK_SET);
00420 fseek(tmp, 0, SEEK_SET);
00421
00422
00423 fwrite(btag_start, tag->header->total_tag_size, 1, tmp);
00424
00425
00426 fwrite(blank, 1024, 1, tmp);
00427
00428
00429 while(!feof(file))
00430 {
00431 read = fread(ptr, 1, 4096, file);
00432 if(fwrite(ptr, 1, read, tmp) != read && !feof(file))
00433 {
00434 fflush(file);
00435 fclose(tmp);
00436 xfree(ptr);
00437 xfree(blank);
00438 goto exit_on_error;
00439 }
00440 }
00441
00442 fflush(tmp);
00443
00444 fseek(file, 0, SEEK_SET);
00445 fseek(tmp, 0, SEEK_SET);
00446 while(!feof(tmp))
00447 {
00448 read = fread(ptr, 1, 4096, tmp);
00449 if(fwrite(ptr, 1, read, file) != read && !feof(tmp))
00450 {
00451 fflush(file);
00452 fclose(tmp);
00453 xfree(ptr);
00454 xfree(blank);
00455 goto exit_on_error;
00456 }
00457 }
00458
00459 fflush(file);
00460 fclose(tmp);
00461
00462 xfree(ptr);
00463 xfree(blank);
00464 }
00465
00466 xfree(btag_start);
00467 return 0;
00468
00469 exit_on_error:
00470 xfree(btag_start);
00471 return MP_EERROR;
00472 }
00473
00474 static int
00475 id3v1_del_tag(int fd)
00476 {
00477 int nlength;
00478 unsigned char *c;
00479 struct stat fs;
00480
00481 if(fstat(fd, &fs)) return 1;
00482
00483 if(fs.st_size < 128) return 1;
00484
00485 c = (char *)xmallocd(3, "id3v1_del_tag:c");
00486 if(lseek(fd, -128L, SEEK_END) == -1) goto exit_on_error;
00487 if(read(fd, c, 3) < 3) goto exit_on_error;
00488 if(strncmp(c, "TAG", 3)) goto exit_on_error;
00489 xfree(c);
00490
00491 nlength = fs.st_size - 128;
00492
00493 if(ftruncate(fd, nlength)) return 1;
00494
00495 return 0;
00496
00497 exit_on_error:
00498 xfree(c);
00499 return 1;
00500 }
00501
00502
00503 static int
00504 id3v2_del_tag(int fd, id3v2_tag *t)
00505 {
00506 unsigned char *c;
00507 long tag_len, file_len;
00508 FILE *file, *tmp;
00509 int read;
00510 void *ptr;
00511 id3v2_tag *tfound = NULL;;
00512 file_arg arg;
00513
00514 if(!t)
00515 {
00516 arg.fd = fd;
00517 t = id3v2_get_tag(read_file, lseek_file, &arg);
00518 if(!t) return 0;
00519 else tfound = t;
00520 }
00521
00522 ptr = xmallocd(4096, "id3v2_del_tag:ptr");
00523
00524 tag_len = t->header->total_tag_size;
00525 file_len = lseek(fd, 0, SEEK_END);
00526 if(file_len < 1 || tag_len < 1) goto exit_on_error;
00527
00528
00529 file = fdopen(fd, "r+b");
00530 tmp = tmpfile();
00531 if(!(file && tmp)) goto exit_on_error;
00532
00533 fseek(file, tag_len, SEEK_SET);
00534 fseek(tmp, 0, SEEK_SET);
00535 while(!feof(file))
00536 {
00537 read = fread(ptr, 1, 4096, file);
00538 if(fwrite(ptr, 1, read, tmp) != read && !feof(file))
00539 goto exit_on_error;
00540 }
00541
00542 fflush(tmp);
00543
00544 fseek(file, 0, SEEK_SET);
00545 fseek(tmp, 0, SEEK_SET);
00546 while(!feof(tmp))
00547 {
00548 read = fread(ptr, 1, 4096, tmp);
00549 if(fwrite(ptr, 1, read, file) != read && !feof(tmp))
00550 goto exit_on_error;
00551 }
00552
00553 fclose(tmp);
00554 xfree(ptr);
00555 if(tfound) id3v2_free_tag(tfound);
00556 return 0;
00557
00558 exit_on_error:
00559 fclose(tmp);
00560 xfree(ptr);
00561 if(tfound) id3v2_free_tag(tfound);
00562 return 1;
00563 }
00564
00565
00566 static int
00567 id3v1_truncate_tag(id3v1_tag *tag)
00568 {
00569 int notrunc = 0;
00570 int len = 0;
00571 void *ptr;
00572
00573 if(tag->title && (len = strlen(tag->title)) > 30)
00574 {
00575 realloc(tag->title, 31);
00576 tag->title[30] = 0;
00577 }
00578
00579 if(tag->artist && (len = strlen(tag->artist)) > 30)
00580 {
00581 realloc(tag->artist, 31);
00582 tag->artist[30] = 0;
00583 }
00584
00585 if(tag->album && (len = strlen(tag->album)) > 30)
00586 {
00587 realloc(tag->album, 31);
00588 tag->album[30] = 0;
00589 }
00590
00591 if(tag->year && (len = strlen(tag->year)) > 4)
00592 {
00593 realloc(tag->title, 5);
00594 tag->title[4] = 0;
00595 }
00596
00597 if(tag->comment)
00598 {
00599 int max = (tag->track > 0) ? 28 : 30;
00600 if((len = strlen(tag->comment)) > max)
00601 {
00602 realloc(tag->comment, max + 1);
00603 tag->comment[max] = 0;
00604 }
00605 }
00606
00607 return notrunc;
00608 }
00609
00610 static int
00611 id3_is_only_space(char *str, int strlen)
00612 {
00613 int i = 0;
00614
00615 while(i < strlen)
00616 {
00617 if(str[i] != 0x20) return 0;
00618 i++;
00619 }
00620
00621 return 1;
00622 }
00623
00624 static id3v2_tag*
00625 id3v2_get_tag(size_t (*read_func)(void *, size_t, void *),
00626 off_t (*lseek_func)(off_t, int, void *),
00627 void *arg)
00628 {
00629 unsigned char *c;
00630 id3v2_header *header;
00631 id3v2_frame_list *frame_list;
00632 id3v2_frame *frame;
00633 id3v2_tag *tag = NULL;
00634 int i;
00635
00636 if(lseek_func(0L, SEEK_SET, arg) == -1) return NULL;
00637
00638 c = (unsigned char*)xmallocd0(1024, "id3v2_get_tag:c");
00639
00640 if(read_func(c, 10, arg) < 10) goto exit_on_error;
00641
00642 c[10] = 0;
00643
00644 if(strncmp(c, "ID3", 3)) goto exit_on_error;
00645
00646 header = XMALLOCD0(id3v2_header, "id3v2_get_tag:header");
00647 header->version_minor = c[3];
00648 header->version_revision = c[4];
00649 header->flags = c[5];
00650 header->unsyncronization = (c[5] & 128) >> 7;
00651 header->has_extended_header = (c[5] & 64) >> 6;
00652 header->is_experimental = (c[5] & 32) >> 5;
00653 header->has_footer = (c[5] & 16) >> 4;
00654
00655 header->total_tag_size = id3_unsync32(c, 6) + 10;
00656 if(header->has_footer) header->total_tag_size += 10;
00657
00658 tag = XMALLOCD0(id3v2_tag, "id3v2_get_tag:tag");
00659
00660
00661 if(c[3] != 3 && c[3] != 4)
00662 {
00663 xfree(c);
00664 tag->header = header;
00665 tag->frame_list = NULL;
00666 return tag;
00667 }
00668
00669 frame_list = XMALLOCD0(id3v2_frame_list, "id3v2_get_tag:frame_list");
00670 frame_list->start = frame_list;
00671
00672
00673 tag->header = header;
00674 tag->frame_list = frame_list;
00675
00676 if(header->has_extended_header)
00677 {
00678 id3v2_extended_header *xt_header = XMALLOCD0(id3v2_extended_header,
00679 "id3v2_get_tag:id3v2_extended_header");
00680
00681 header->extended_header = xt_header;
00682
00683 read_func(c, 4, arg);
00684 xt_header->size = id3_unsync32(c, 0);
00685
00686 read_func(c, 1, arg);
00687 xt_header->no_flag_bytes = (c[0] > 0) ? c[0] : 1;
00688
00689 read_func(c, xt_header->no_flag_bytes, arg);
00690 xt_header->is_update = (c[0] & 64) >> 6;
00691 xt_header->crc_data_present = (c[0] & 32) >> 5;
00692 xt_header->restrictions = (c[0] & 16) >> 4;
00693
00694
00695 if(xt_header->is_update) read_func(c, 1, arg);
00696 if(xt_header->crc_data_present) {
00697 read_func(c, 1, arg);
00698 if(*c != 5) goto exit_on_error;
00699
00700 xt_header->crc_data_length = *c;
00701 xt_header->crc_data = xmallocd0(*c, "id3v2_get_tag:xt_header->crc_data");
00702 read_func(xt_header->crc_data, *c, arg);
00703 }
00704 if(xt_header->restrictions) {
00705 read_func(c, 1, arg);
00706 if(*c != 1) goto exit_on_error;
00707 xt_header->restrictions_data_length = *c;
00708 xt_header->restrictions_data = xmallocd0(*c,
00709 "id3v2_get_tag:xt_header->restrictions_data");
00710 read_func(xt_header->restrictions_data, *c, arg);
00711 }
00712 }
00713
00714
00715 while(lseek_func(0L, SEEK_CUR, arg) < header->total_tag_size)
00716 {
00717 int hasEnc = 0, hasLang = 0, d;
00718 unsigned int data_size;
00719
00720 read_func(c, 10, arg);
00721
00722
00723 if(c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0) break;
00724
00725
00726 if(!isalnum(c[0]) || !isalnum(c[1]) || !isalnum(c[2]) || !isalnum(c[3])) break;
00727
00728 data_size = id3_unsync32(c, 4);
00729
00730
00731 if (data_size > header->total_tag_size) break;
00732
00733 frame = XMALLOCD(id3v2_frame, "id3v2_get_tag:frame");
00734 frame->frame_id = xmallocd(4, "id3v2_get_tag:frame->frame_id");
00735 strncpy(frame->frame_id, c, 4);
00736 frame->data_size = data_size;
00737 frame->status_flag = c[8];
00738 frame->format_flag = c[9];
00739
00740
00741 frame->data = xmallocd(frame->data_size, "id3v2_get_tag:frame->data_size");
00742 read_func(frame->data, frame->data_size, arg);
00743
00744
00745 if(frame_list->data)
00746 {
00747 frame_list->next = XMALLOCD(id3v2_frame_list, "id3v2_get_tag:frame_list->next");
00748 frame_list->next->start = frame_list->start;
00749 frame_list = frame_list->next;
00750 frame_list->next = NULL;
00751 }
00752 frame_list->data = frame;
00753 }
00754
00755
00756 xfree(c);
00757 return tag;
00758
00759 exit_on_error:
00760
00761 xfree(c);
00762 id3v2_free_tag(tag);
00763 return NULL;
00764 }
00765
00766 static char **
00767 id3v2_get_names(id3v2_tag *tag)
00768 {
00769 id3v2_frame *frame;
00770 id3v2_frame_list *frame_list;
00771 char **clist;
00772 int i;
00773
00774 if(!tag->frame_list) return NULL;
00775
00776 frame_list = tag->frame_list;
00777
00778 i = id3_get_no_frames(tag);
00779 clist = xmallocd(sizeof(char*) * i+1, "id3v2_get_names:clist");
00780 clist[i] = 0;
00781
00782 for(i = 0; frame_list; i++)
00783 {
00784 if(!frame_list->data) continue;
00785 frame = frame_list->data;
00786
00787 if(!frame->frame_id) continue;
00788 clist[i] = xmallocd(5, "id3v2_get_names:clist[i]");
00789 strncpy(clist[i], frame->frame_id, 4);
00790 clist[i][4] = 0;
00791 frame_list = frame_list->next;
00792 }
00793 return clist;
00794 }
00795
00796
00797 static id3_content*
00798 id3v1_get_content(id3v1_tag *tag, int field)
00799 {
00800 int i;
00801 char *c;
00802 id3_content *ret;
00803
00804 switch(field)
00805 {
00806 case MP_ARTIST:
00807 if(!tag->artist)
00808 {
00809 errno = MP_EFNF;
00810 return NULL;
00811 }
00812 return mp_assemble_text_content(tag->artist, ISO_8859_1);
00813
00814 case MP_TITLE:
00815 if(!tag->title)
00816 {
00817 errno = MP_EFNF;
00818 return NULL;
00819 }
00820 return mp_assemble_text_content(tag->title, ISO_8859_1);
00821
00822 case MP_ALBUM:
00823 if(!tag->album)
00824 {
00825 errno = MP_EFNF;
00826 return NULL;
00827 }
00828 return mp_assemble_text_content(tag->album, ISO_8859_1);
00829
00830 case MP_YEAR:
00831 if(!tag->year)
00832 {
00833 errno = MP_EFNF;
00834 return NULL;
00835 }
00836 return mp_assemble_text_content(tag->year, ISO_8859_1);
00837
00838 case MP_COMMENT:
00839 if(!tag->comment)
00840 {
00841 errno = MP_EFNF;
00842 return NULL;
00843 }
00844 return mp_assemble_comment_content(tag->comment, NULL, ISO_8859_1, NULL);
00845
00846 case MP_GENRE:
00847 if(tag->genre < GLL)
00848 {
00849 return mp_assemble_text_content(genre_list[tag->genre], ISO_8859_1);
00850 }
00851 else
00852 {
00853 errno = MP_EFNF;
00854 return NULL;
00855 }
00856
00857 case MP_TRACK:
00858 if(!tag->track)
00859 {
00860 errno = MP_EFNF;
00861 return NULL;
00862 }
00863
00864 if(tag->track < 10) i = 2;
00865 else if(tag->track < 100) i = 3;
00866 else i = 4;
00867 c = xmallocd(i, "id3v1_get_content:c");
00868 snprintf(c, i, "%d", tag->track);
00869 ret = mp_assemble_text_content(c, ISO_8859_1);
00870 xfree(c);
00871 return ret;
00872
00873 default:
00874 errno = MP_EFNF;
00875 return NULL;
00876 }
00877 }
00878
00879 static id3_content*
00880 id3v2_get_content_at_pos(id3v2_tag *tag, const char *field, int pos)
00881 {
00882 id3v2_frame_list *frame_list;
00883 id3v2_frame *frame;
00884 int i, found, j;
00885
00886 if(!tag->frame_list || !field)
00887 {
00888 errno = MP_EERROR;
00889 return NULL;
00890 }
00891
00892 frame_list = tag->frame_list;
00893
00894 for(i = 0, found = 0; frame_list; i++, frame_list = frame_list->next)
00895 {
00896 if(!frame_list->data) continue;
00897 frame = frame_list->data;
00898
00899 if(!frame->frame_id || !frame->data) continue;
00900 if(strncmp(frame->frame_id, field, 4)) continue;
00901
00902 if(found == pos)
00903 {
00904 id3_content *ret = XMALLOCD0(id3_content,
00905 "id3v2_get_content_at_pos:ret");
00906 if(frame_is_compressed(frame)) ret->compressed = 1;
00907 else ret->compressed = 0;
00908
00909 if(frame_is_encrypted(frame)) ret->encrypted = 1;
00910 else ret->encrypted = 0;
00911
00912 ret->length = frame->data_size;
00913 ret->data = xmallocd(frame->data_size, "id3v2_get_content_at_pos:ret->data");
00914 ret->data = memcpy(ret->data, frame->data, frame->data_size);
00915
00916 return ret;
00917 }
00918
00919 found++;
00920 }
00921
00922 errno = MP_EFNF;
00923 return NULL;
00924 }
00925
00926 static long
00927 id3_sync(unsigned char* data, long length)
00928 {
00929 int i;
00930
00931 for(i = 0; i < length - 1; i++)
00932 {
00933 if(((data[i] & 0xFF) == 0xFF && (data[i+1] & 0xE0) == 0xE0) ||
00934 (i+2 < length && (data[i] & 0xFF) == 0xFF && data[i+1] == 0 && data[i+2] != 0))
00935 {
00936
00937 realloc(data, length + 1); length++;
00938 memmove(data + i+2, data + i+1, length - i - 2);
00939 memset(data + i+1, 0, 1);
00940 }
00941 }
00942
00943 return length;
00944 }
00945
00946 static long
00947 id3_unsync(unsigned char* data, long length)
00948 {
00949
00950
00951
00952
00953
00954
00955 }
00956
00957 static unsigned int
00958 id3_unsync32(unsigned char* c, int start)
00959 {
00960 return c[start+3] + (c[start+2] << 7) + (c[start+1] << 14) + (c[start] << 21);
00961 }
00962
00963 int
00964 id3_get_no_frames(id3v2_tag *tag)
00965 {
00966 int i;
00967 id3v2_frame_list *frame_list = tag->frame_list;
00968
00969 for(i = 0; frame_list; i++)
00970 frame_list = frame_list->next;
00971
00972 return i;
00973 }
00974
00975 static unsigned char *
00976 id3_sync32(unsigned int i)
00977 {
00978 unsigned char *c = (unsigned char *)xmallocd0(4, "id3_sync32:c");
00979
00980 c[3] = (i & 0x7f);
00981 c[2] = ((i & 0x80) >> 7) | (((i & 0x7f00) >> 8) << 1);
00982 c[1] = ((i & 0x8000) >> 15) | (((i & 0x7f0000) >> 16) << 1);
00983 c[0] = ((i & 0x800000) >> 23) | (((i & 0x7f000000) >> 24) << 1);
00984
00985 return c;
00986 }
00987
00988 id3v2_frame*
00989 id3_lookup_frame(id3v2_frame_list *list, const char *field, const int pos)
00990 {
00991 int cur = 0;
00992
00993 if(!list || !field) return NULL;
00994
00995 do
00996 {
00997 if(!strcmp(list->data->frame_id, field))
00998 {
00999 if(cur == pos) return list->data;
01000 cur++;
01001 }
01002 } while((list = list->next));
01003
01004 return NULL;
01005 }
01006
01007 int
01008 id3_remove_frame(id3v2_frame_list *list, id3v2_frame *frame)
01009 {
01010 if(!list || !frame) return MP_EERROR;
01011
01012
01013 if(list->data == frame)
01014 {
01015 xfree(list->data);
01016 list->next->start = list->next;
01017 xfree(list);
01018 return 0;
01019 }
01020
01021
01022 do
01023 {
01024 if(list->next->data == frame)
01025 {
01026 list->next = list->next->next;
01027 xfree(frame);
01028 return 0;
01029 }
01030 } while((list = list->next));
01031
01032 return 1;
01033 }
01034
01035
01036 int
01037 id3_add_frame(id3v2_frame_list *list, char *field, char *new_value, int len)
01038 {
01039 id3v2_frame *frame;
01040 char *new_valuecp;
01041 long len_sync;
01042
01043 if(!list || !new_value || !field || strlen(field) != 4) return MP_EERROR;
01044
01045
01046 new_valuecp = xmallocd(len,
01047 "id3_add_frame:new_valuecp");
01048 memcpy(new_valuecp, new_value, len);
01049 new_value = new_valuecp;
01050
01051
01052 len_sync = id3_sync(new_value, len);
01053
01054 frame = XMALLOCD(id3v2_frame, "id3_add_frame:frame");
01055 frame->frame_id = xmallocd(4, "id3_add_frame:frame->frame_id");
01056 strncpy(frame->frame_id, field, 4);
01057 frame->data = new_value;
01058 frame->status_flag = 0;
01059
01060 if(len != len_sync) frame->format_flag = 64;
01061 else frame->format_flag = 0;
01062 frame->data_size = len_sync;
01063
01064
01065 if(!list->data)
01066 {
01067 list->data = frame;
01068 return 0;
01069 }
01070
01071
01072 while(list->next) list = list->next;
01073
01074 list->next = XMALLOCD(id3v2_frame_list, "id3_add_frame:list->next");
01075 list->next->start = list->start;
01076 list = list->next;
01077 list->next = NULL;
01078 list->data = frame;
01079
01080 return 0;
01081 }
01082
01083
01084 #define BUF_SIZE 4096
01085
01086 static int
01087 id3_lseek_syncword(int fd)
01088 {
01089 unsigned char *data = (unsigned char*) xmallocd(BUF_SIZE, "id3_lseek_syncword:data");
01090 int ret;
01091
01092
01093 lseek(fd, SEEK_SET, 0);
01094
01095 if(read(fd, data, BUF_SIZE) < 1)
01096 {
01097 xfree(data);
01098 return 0;
01099 }
01100
01101 ret = id3_lseek_syncword_r(fd, data, 0);
01102 xfree(data);
01103 return ret;
01104 }
01105
01106 static int
01107 id3_lseek_syncword_r(int fd, unsigned char *data, int checked)
01108 {
01109 unsigned char lastchar;
01110 int i;
01111
01112 for(i = 0; i + 1 < BUF_SIZE; i++)
01113 {
01114 if(((data[i] & 0xFF)== 0xFF) && (((data[i+1] & 0xF0) == 0xF0) || ((data[i+1] == 0xE0) & 0xE0)))
01115 {
01116 lseek(fd, checked + i, SEEK_SET);
01117 return 0;
01118 }
01119 }
01120
01121 lastchar = data[BUF_SIZE - 1];
01122
01123 if(read(fd, data, BUF_SIZE) < 1) return 0;
01124
01125 if(((lastchar & 0xFF)== 0xFF) && ((data[0] & 0xE0)== 0xE0))
01126 {
01127 lseek(fd, checked + BUF_SIZE - 1, SEEK_SET);
01128 return 0;
01129 }
01130
01131 return id3_lseek_syncword_r(fd, data, checked + BUF_SIZE);
01132 }
01133
01134 static id3_tag*
01135 id3v1_alloc_tag(void)
01136 {
01137 id3_tag *tag = XMALLOCD(id3_tag, "id3v1_alloc_tag:tag");
01138 id3v1_tag *v1 = XMALLOCD0(id3v1_tag, "id3v1_alloc_tag:v1");
01139
01140 tag->tag = v1;
01141 tag->version = 1;
01142
01143 v1->genre = 0xFF;
01144
01145 return tag;
01146 }
01147
01148 static id3_tag*
01149 id3v2_alloc_tag(void)
01150 {
01151 id3_tag *tag = XMALLOCD(id3_tag, "id3v2_alloc_tag:tag");
01152 id3v2_tag *v2 = XMALLOCD0(id3v2_tag, "id3v2_alloc_tag:v2");
01153
01154 tag->tag = v2;
01155 tag->version = 2;
01156
01157 return tag;
01158 }
01159
01160 static void
01161 id3v1_free_tag(id3v1_tag* v1)
01162 {
01163 xfree(v1->artist);
01164 xfree(v1->album);
01165 xfree(v1->title);
01166 xfree(v1->year);
01167 xfree(v1->comment);
01168 xfree(v1);
01169 }
01170
01171 static void
01172 id3v2_free_tag(id3v2_tag* v2)
01173 {
01174 id3v2_frame_list* doomed;
01175 id3v2_frame *frame;
01176
01177 if(!v2) return;
01178
01179 xfree(v2->header->extended_header);
01180 xfree(v2->header);
01181
01182 if(!v2->frame_list)
01183 {
01184 xfree(v2);
01185 return;
01186 }
01187
01188
01189 do
01190 {
01191 frame = (id3v2_frame*)v2->frame_list->data;
01192 if(frame)
01193 {
01194 if(frame->frame_id) xfree(frame->frame_id);
01195 if(frame->data) xfree(frame->data);
01196 }
01197 xfree(v2->frame_list->data);
01198
01199 doomed = v2->frame_list->next;
01200 xfree(v2->frame_list);
01201
01202 } while((v2->frame_list = doomed));
01203
01204 xfree(v2);
01205 }
01206
01207 size_t read_mem(void *dest, size_t nbytes, void *varg) {
01208 size_t count;
01209 mem_arg *arg = (mem_arg *)varg;
01210
01211 if (arg->act - arg->buf + nbytes <= arg->size) {
01212 memcpy(dest, arg->act, nbytes);
01213 arg->act += nbytes;
01214
01215 return nbytes;
01216 } else {
01217 count = arg->size - (arg->act - arg->buf);
01218 memcpy(dest, arg->act, count);
01219 arg->act += count;
01220
01221 return count;
01222 }
01223 }
01224
01225 size_t read_file(void *dest, size_t nbytes, void *varg) {
01226 file_arg *arg = (file_arg *)varg;
01227
01228 return read(arg->fd, dest, nbytes);
01229 }
01230
01231 off_t lseek_mem(off_t offset, int whence, void *varg) {
01232 mem_arg *arg = (mem_arg *)varg;
01233 off_t retval;
01234
01235 if (!arg->buf) {
01236 errno = EBADF;
01237 return (off_t)-1;
01238 }
01239
01240 switch (whence) {
01241 case SEEK_SET:
01242 if (offset >= 0)
01243 arg->act = arg->buf + offset;
01244 else goto exit_on_error;
01245 break;
01246 case SEEK_CUR:
01247 if (arg->act + offset >= arg->buf)
01248 arg->act += offset;
01249 else goto exit_on_error;
01250 break;
01251 case SEEK_END:
01252 if (arg->buf + arg->size + offset >= arg->buf)
01253 arg->act = arg->buf + arg->size + offset;
01254 else goto exit_on_error;
01255 break;
01256 default:
01257 goto exit_on_error;
01258 }
01259
01260 return (arg->act - arg->buf);
01261
01262 exit_on_error:
01263 errno = EINVAL;
01264 return (off_t)-1;
01265 }
01266
01267 off_t lseek_file(off_t offset, int whence, void *varg) {
01268 file_arg *arg = (file_arg *)varg;
01269
01270 return lseek(arg->fd, offset, whence);
01271 }
01272