mplib.c

Go to the documentation of this file.
00001 /*
00002  * mplib - a library that enables you to edit ID3 tags
00003  *
00004  * Copyright (c) 2001,2002,2003,2004,2005,2006 Stefan Podkowinski
00005  *               2006                          Michal Kowalczuk
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without 
00009  * modification, are permitted provided that the following conditions are met:
00010  *
00011  * 1. Redistributions of source code must retain the above copyright notice, this
00012  *    list of conditions and the following disclaimer.
00013  * 2. Redistributions in binary form must reproduce the above copyright notice,
00014  *    this list of conditions and the following disclaimer in the documentation
00015  *    and/or other materials provided with the distribution.
00016  * 3. Neither the name of the author nor the names of its contributors
00017  *    may be used to endorse or promote products derived from this software
00018  *    without specific prior written permission.
00019  *
00020  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00021  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00022  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00023  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00024  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00025  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00026  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00027  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00028  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00029  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00030  * POSSIBILITY OF SUCH DAMAGE.
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 /*STDC_HEADERS*/
00043 
00044 #if HAVE_UNISTD_H
00045 # include <unistd.h>
00046 # include <sys/types.h>
00047 #endif
00048 
00049 #include <errno.h>
00050 #include <stdio.h>
00051 #include <fcntl.h>
00052 
00053 #include "xmalloc.h"
00054 #include "mplib.h"
00055 #include "mplib_s.h"
00056 #include "mplib_s.c"
00057 
00058 
00059 
00060 /*******************************************************************************************
00061  *                                   Extern functions
00062  *******************************************************************************************/
00063 
00064 
00065 
00066 /*******************************************************************************************
00067  *                                         Get
00068  *******************************************************************************************/
00069 
00070  mpeg_header*
00071  mp_get_mpeg_header_from_file(const char* filename)
00072  {
00073          mpeg_header *ret;
00074          int fd;
00075          
00076          if(!filename) return NULL;
00077          
00078          fd = open(filename, O_RDONLY);
00079          if(fd == -1) return NULL;
00080          
00081          ret = mp_get_mpeg_header_from_fd(fd);
00082          close(fd);
00083          return ret;
00084  }
00085  
00086 
00087 mpeg_header*
00088 mp_get_mpeg_header_from_fd(int fd)
00089 {
00090         mpeg_header *h;
00091         unsigned char c[5];
00092         
00093         h = XMALLOCD(mpeg_header, "mp_get_mpeg_header_from_fd:h");
00094         
00095         if(id3_lseek_syncword(fd)) goto exit_on_error;
00096         
00097         if(read(fd, c, 4) < 4) goto exit_on_error;
00098         
00099         memset(h, 0, sizeof(h));
00100         h->syncword = (c[1] & 240);
00101         h->syncword <<= 8;
00102         h->syncword |= c[0]; 
00103         h->version = (c[1] & 8) >> 3;
00104         h->layer = (c[1] & 6) >> 1;
00105         h->protbit = (c[1] & 1);
00106         h->bitrate = (c[2] & 240) >> 4;
00107         h->samplingfreq = (c[2] & 12) >> 2;
00108         h->padbit = (c[2] & 2) >> 1;
00109         h->privbit = (c[2] & 1);
00110         h->mode = (c[3] & 192) >> 6;
00111         h->mode_ext = (c[3] & 48) >> 4;
00112         h->copyright = (c[3] & 8) >> 3;
00113         h->originalhome = (c[3] & 4) >> 2;
00114         h->emphasis = (c[3] & 3);
00115         
00116         return h;
00117 
00118  exit_on_error:
00119         xfree(h);
00120         return NULL;
00121 }
00122 
00123 char*
00124 mp_get_str_version(const mpeg_header *h) 
00125 {
00126         return h->version == 0 ? "MPEG 2" : "MPEG 1";
00127 }
00128 
00129 char* 
00130 mp_get_str_layer(const mpeg_header *h) 
00131 {
00132         switch(h->layer) 
00133         {
00134                 case 1: return "Layer III";
00135                 case 2: return "Layer II";
00136                 case 3: return "Layer I";
00137                 default: return "undefined";
00138         }
00139 }
00140 
00141 char*
00142 mp_get_str_bitrate(const mpeg_header *h) 
00143 {
00144         char *buf = (char *)xmallocd0(11, "mp_get_str_bitrate:buf");
00145         
00146         if(h->version == 1) /* MPEG 1 */
00147         { 
00148                 switch(h->layer) 
00149                 {
00150                         case 1:
00151                                 snprintf(buf, sizeof buf, "%d kBit/s", br_1_3[h->bitrate]);
00152                                 return buf;
00153                         case 2:
00154                                 snprintf(buf, sizeof buf, "%d kBit/s", br_1_2[h->bitrate]);
00155                                 return buf;
00156                         case 3:
00157                                 snprintf(buf, sizeof buf, "%d kBit/s", br_1_1[h->bitrate]);
00158                                 return buf;
00159                         default:
00160                                 return "undefined";
00161                 }
00162         } 
00163         else /* MPEG 2 */
00164         { 
00165                 switch(h->layer) 
00166                 {
00167                         case 1:
00168                                 snprintf(buf, sizeof buf, "%d kBit/s", br_2_3[h->bitrate]);
00169                                 return buf;
00170                         case 2:
00171                                 snprintf(buf, sizeof buf, "%d kBit/s", br_2_2[h->bitrate]);
00172                                 return buf;
00173                         case 3:
00174                                 snprintf(buf, sizeof buf, "%d kBit/s", br_2_1[h->bitrate]);
00175                                 return buf;
00176                         default:
00177                                 return "undefined";
00178                 }
00179         }
00180 }
00181 
00182 char*
00183 mp_get_str_samplingfreq(const mpeg_header *h) 
00184 {
00185         if(h->version == 1) 
00186         {
00187                 switch(h->samplingfreq) 
00188                 {
00189                         case 0: return "44100 Hz";
00190                         case 1: return "48000 Hz";
00191                         case 2: return "32000 Hz";
00192                         default: return "undefined";
00193                 }
00194         } 
00195         else 
00196         {
00197                 switch(h->samplingfreq) 
00198                 {
00199                         case 0: return "22050 Hz";
00200                         case 1: return "24000 Hz";
00201                         case 2: return "16000 Hz";
00202                         default: return "undefined";
00203                 }
00204         }
00205 }
00206 
00207 char*
00208 mp_get_str_mode(const mpeg_header *h) 
00209 {
00210         switch(h->mode) 
00211         {
00212                 case 0: return "Stereo";
00213                 case 1: return "Joint-Stereo";
00214                 case 2: return "Dual-Channel";
00215                 case 3: return "Mono";
00216                 default: return "undefined";
00217         }
00218 }
00219 
00220 id3_tag_list* 
00221 mp_get_tag_list_from_mem(void *buf, size_t size)
00222 {
00223         mem_arg arg;
00224 
00225         arg.buf = buf;
00226         arg.size = size;
00227         arg.act = buf;
00228 
00229         return mp_get_tag_list(read_mem, lseek_mem, &arg);
00230 }
00231 
00232 id3_tag_list* 
00233 mp_get_tag_list_from_file(const char* filename)
00234 {
00235         id3_tag_list *ret;
00236         int fd;
00237         
00238         if(!filename) return NULL;
00239         
00240         fd = open(filename, O_RDONLY);
00241         if(fd == -1) return NULL;
00242     
00243         ret = mp_get_tag_list_from_fd(fd);
00244     
00245         close(fd);
00246         return ret;
00247 }
00248 
00249 id3_tag_list* 
00250 mp_get_tag_list_from_fd(int fd)
00251 {
00252     file_arg arg;
00253     
00254     arg.fd = fd;
00255     return mp_get_tag_list(read_file, lseek_file, &arg);
00256 }
00257 
00258 id3_tag_list* 
00259 mp_get_tag_list(size_t (*read_func)(void *, size_t, void *),
00260                 off_t (*lseek_func)(off_t, int, void *),
00261                 void *arg)
00262 {
00263         id3_tag_list *tag_list = NULL;
00264         id3_tag_list *tag_list2 = NULL;
00265         id3v2_tag *v2tag = NULL;
00266         id3v1_tag *v1tag = NULL;
00267         id3_tag *tag = NULL;
00268         
00269         v2tag = id3v2_get_tag(read_func, lseek_func, arg);
00270         if(v2tag)
00271         {
00272                 tag = XMALLOCD0(id3_tag, "mp_get_tag_list:tag");
00273                 if(v2tag->header->version_minor == 3 || v2tag->header->version_minor == 4)
00274                         tag->version = 2;
00275                 else
00276                         tag->version = -1;
00277                 tag->tag = v2tag;
00278                         
00279                 tag_list = XMALLOCD(id3_tag_list, "mp_get_tag_list:tag_list");
00280                 tag_list->tag = tag;
00281                 tag_list->next = NULL;
00282                 tag_list->first = tag_list;
00283         }
00284         
00285         v1tag = id3v1_get_tag(read_func, lseek_func, arg);
00286         if(v1tag)
00287         {
00288                 tag = XMALLOCD(id3_tag, "mp_get_tag_list:tag");
00289                 tag->version = 1;
00290                 tag->tag = v1tag;
00291                 
00292                 if(tag_list)
00293                 {
00294                         tag_list2 = XMALLOCD(id3_tag_list, "mp_get_tag_list:tag_list2");
00295                         tag_list2->tag = tag;
00296                         tag_list2->next = NULL;
00297                         tag_list2->first = tag_list;
00298                         tag_list->next = tag_list2;
00299                 }
00300                 else
00301                 {
00302                         tag_list = XMALLOCD(id3_tag_list, "mp_get_tag_list:tag_list");
00303                         tag_list->tag = tag;
00304                         tag_list->next = NULL;
00305                         tag_list->first = tag_list;
00306                 }
00307         }
00308         
00309         return tag_list;
00310 
00311 }
00312         
00313 id3_content*
00314 mp_get_content(const id3_tag *tag, int field)
00315 {
00316         return mp_get_content_at_pos(tag, field, 0);
00317 }
00318 
00319 id3_content*
00320 mp_get_content_at_pos(const id3_tag *tag, int field, int pos)
00321 {
00322         int i;
00323         char *c;
00324         id3_content *ret;
00325         
00326         if(!tag || !tag->tag)
00327         {
00328                 errno = MP_EERROR;
00329                 return NULL;
00330         }
00331         
00332         if(tag->version == 1)
00333         {
00334                 if(pos != 0)
00335                 {
00336                         errno = MP_EERROR;
00337                         return NULL; 
00338                 }
00339                 else return id3v1_get_content(tag->tag, field);
00340         }
00341         else if(tag->version == 2)
00342         {
00343                 id3v2_tag *v2 = tag->tag;
00344                 char *val;
00345                 
00346                 switch(field)
00347                 {
00348                         case MP_ARTIST:
00349                                 return mp_get_content_custom_at_pos(tag, "TPE1", pos);
00350                         case MP_TITLE:
00351                                 return mp_get_content_custom_at_pos(tag, "TIT2", pos);
00352                         case MP_ALBUM:
00353                                 return mp_get_content_custom_at_pos(tag, "TALB", pos);
00354                         case MP_GENRE:
00355                                 return mp_get_content_custom_at_pos(tag, "TCON", pos);
00356                         case MP_COMMENT:
00357                                 return mp_get_content_custom_at_pos(tag, "COMM", pos);
00358                         case MP_YEAR:
00359                                 return mp_get_content_custom_at_pos(tag, "TYER", pos);
00360                         case MP_TRACK:
00361                                 return mp_get_content_custom_at_pos(tag, "TRCK", pos);
00362                 }
00363                 errno = MP_EFNF;
00364                 return NULL;
00365         }
00366         else
00367         {
00368                 errno = MP_EVERSION;
00369                 return NULL;
00370         }
00371 }
00372 
00373 id3_content*
00374 mp_get_content_custom(const id3_tag* tag, const char*field)
00375 {
00376         if(!tag)
00377         {
00378                 errno = MP_EERROR;
00379                 return NULL;
00380         } 
00381         else if(tag->version != 2)
00382         {               
00383                 errno = MP_EVERSION;
00384                 return NULL;
00385         }
00386         
00387         return id3v2_get_content_at_pos(tag->tag, field, 0);
00388         
00389 }
00390 
00391 id3_content*
00392 mp_get_content_custom_at_pos(const id3_tag* tag, const char*field, int pos)
00393 {
00394         if(!tag)
00395         {
00396                 errno = MP_EERROR;
00397                 return NULL;
00398         } 
00399         else if(tag->version != 2)
00400         {               
00401                 errno = MP_EVERSION;
00402                 return NULL;
00403         }
00404         
00405         
00406         return id3v2_get_content_at_pos(tag->tag, field, pos);
00407 }
00408 
00409 
00410 /*******************************************************************************************
00411  *                                         Set
00412  *******************************************************************************************/
00413 
00414  int
00415  mp_set_content(id3_tag* tag, const int field, id3_content* new_content)
00416  {
00417          id3v1_tag *v1;
00418          id3v2_tag *v2;
00419          
00420          
00421          if(!tag) return MP_EERROR;
00422          
00423          if(tag->version == 2)
00424          {
00425                  return mp_set_content_at_pos(tag, field, new_content, 0);
00426          }
00427          else if(tag->version == 1)
00428          {
00429                 unsigned char c;
00430                 char *my_val;
00431                 int len, j;
00432                  
00433                 v1 = tag->tag;
00434                  
00435                  switch(field)
00436                  {
00437 
00438 #define FLD(str1, str2, str3, str4) \
00439                         case str1:\
00440                                 if(!new_content) v1->str2 = NULL;\
00441                                 else\
00442                                 {\
00443                                         id3_text_content *tc = str4(new_content);\
00444                                         if(strlen(tc->text) > str3 || tc->encoding != ISO_8859_1)\
00445                                         {\
00446                                                 mp_convert_to_v2(tag);\
00447                                                 mp_free_text_content(tc);\
00448                                                 return mp_set_content(tag, field, new_content);\
00449                                         }\
00450                                         \
00451                                         v1->str2 = tc->text;\
00452                                         xfree(tc);\
00453                                 }\
00454                                 break;
00455                          
00456                          FLD(MP_ARTIST, artist, 30, mp_parse_artist);
00457                          FLD(MP_TITLE, title, 30, mp_parse_title);
00458                          FLD(MP_ALBUM, album, 30, mp_parse_album);
00459                          FLD(MP_YEAR, year, 4, mp_parse_year);
00460                                  
00461                          case MP_COMMENT:
00462                                  if(!new_content) v1->comment = NULL;
00463                                  else
00464                                  {
00465                                          id3_comment_content *tc = mp_parse_comment(new_content);
00466                                          if(strlen(tc->text) > 30 || tc->short_descr || tc->encoding != ISO_8859_1)
00467                                          {
00468                                                  mp_convert_to_v2(tag);
00469                                                  mp_free_comment_content(tc);
00470                                                  return mp_set_content(tag, field, new_content);
00471                                          }
00472                                          v1->comment = xmallocd0(strlen(tc->text)+1,
00473                                                        "mp_set_content:v1->comment");
00474                                          memcpy(v1->comment, tc->text, strlen(tc->text));
00475                                          mp_free_comment_content(tc);
00476                                  }
00477                                  break;
00478 
00479                          case MP_TRACK:
00480                                  if(!new_content) v1->track = 0;
00481                                  else
00482                                  {
00483                                          id3_text_content *tc = mp_parse_track(new_content);
00484 #ifdef HAVE_STRTOL
00485                                          errno = 0;
00486                                          j = strtol(tc->text, (char **)NULL, 10);
00487                                          if(errno != ERANGE) v1->track = j;
00488                                          else return MP_EERROR;
00489 #else
00490                                          v1->track = atoi(tc->text);
00491 #endif
00492                                          mp_free_text_content(tc);
00493                                  }
00494                                  break;
00495 
00496                          case MP_GENRE:
00497                                  if(!new_content) v1->genre = 0xFF;
00498                                  else
00499                                  {
00500                                          int b = 0, i;
00501                                          id3_text_content *tc = mp_parse_genre(new_content);
00502                                          /* i = strlen(tc->text); */
00503                                          for(c = 0; c < GLL; c++) {
00504                                                  if(!strcmp(genre_list[c], tc->text))
00505                                                  {
00506                                                          v1->genre = c;
00507                                                          b = 1;
00508                                                  }
00509                                          }
00510                                          mp_free_text_content(tc);
00511                                          if(!b)
00512                                          {
00513                                                  mp_convert_to_v2(tag);
00514                                                  return mp_set_content(tag, field, new_content);
00515                                          }
00516                                          break;
00517                                  }
00518                          }
00519          }
00520          else if(tag->version == -1) return MP_EVERSION;
00521          else return MP_EFNF;
00522          
00523          return 0;
00524  }
00525 
00526 int
00527 mp_set_content_at_pos(id3_tag* tag, const int field, id3_content* new_content, int pos)
00528 {
00529         char* c;
00530         
00531         if(!tag) return MP_EERROR;
00532         if(field < MP_ARTIST || field > MP_TRACK) return MP_EFNF;
00533         
00534         if(tag->version == 1 && pos == 0) return mp_set_content(tag, field, new_content);
00535 
00536         switch(field)
00537         {
00538                 case MP_ARTIST: c = "TPE1"; break;
00539                 case MP_TITLE: c = "TIT2"; break;
00540                 case MP_ALBUM: c = "TALB"; break;
00541                 case MP_TRACK: c = "TRCK"; break;
00542                 case MP_YEAR: c = "TYER"; break;
00543                 case MP_COMMENT: c = "COMM"; break;
00544                 case MP_GENRE: c = "TCON"; break;
00545         }
00546         return mp_set_custom_content_at_pos(tag, c, new_content, pos);
00547 }
00548 
00549 int 
00550 mp_set_custom_content(id3_tag* tag, char* field, id3_content* new_content)
00551 {
00552         return mp_set_custom_content_at_pos(tag, field, new_content, 0);
00553 }
00554 
00555 int 
00556 mp_set_custom_content_at_pos(id3_tag* tag, char* field, id3_content* new_content, int pos)
00557 {
00558         id3v2_tag *v2;
00559         
00560         if(!tag || !field || strlen(field) != 4) return MP_EERROR;
00561         
00562         if(tag->version == 1)
00563         {
00564                 if(mp_convert_to_v2(tag))
00565                         return MP_EERROR;
00566         }
00567         else if(tag->version == -1) return MP_EVERSION;
00568         
00569         v2 = (id3v2_tag*)tag->tag;
00570         if(!v2->frame_list)
00571         {
00572                 v2->frame_list = XMALLOCD0(id3v2_frame_list, 
00573                                  "mp_set_custom_content_at_pos:v2->frame_list");
00574                 id3_add_frame(v2->frame_list, field, new_content->data, new_content->length);
00575         }
00576         else
00577         {
00578                 id3v2_frame *frame;
00579                 
00580                 if((frame = id3_lookup_frame(v2->frame_list, field, pos)))
00581                 {
00582                         if(new_content) 
00583                         {
00584                                 long len, len_sync;
00585                                 /* make sync safe */
00586                                 len = new_content->length;
00587                                 len_sync = id3_sync(new_content->data, len);
00588                                 
00589                                 xfree(frame->data);
00590                                 frame->data = xmallocd(new_content->length,
00591                                               "mp_set_custom_content_at_pos:frame->data");
00592                                 memcpy(frame->data, new_content->data, new_content->length);
00593                                 frame->status_flag = 0;
00594                                 if(len != len_sync) frame->format_flag = 64;
00595                                 else frame->format_flag = 0;
00596                                 frame->data_size = len_sync;
00597                         }
00598                         else id3_remove_frame(v2->frame_list, frame);
00599                 }
00600                 else if(pos == 0) id3_add_frame(v2->frame_list, field, new_content->data, new_content->length);
00601                 else return MP_EFNF;
00602         }
00603         
00604         return 0;
00605 }
00606 
00607 /*******************************************************************************************
00608  *                                   Write & delete
00609  *******************************************************************************************/
00610  int
00611  mp_write_to_file(const id3_tag_list* tag_list, const char *filename)
00612  {
00613          int ret;
00614          int fd;
00615          
00616          if(!filename) return MP_EERROR;
00617          
00618          fd = open(filename, O_RDWR);
00619          if(fd == -1) return MP_EERROR;
00620          
00621          ret = mp_write_to_fd(tag_list, fd);
00622          close(fd);
00623          return ret;
00624  }
00625  
00626  
00627  int
00628  mp_write_to_fd(const id3_tag_list* tag_list, const int fd)
00629  {
00630          id3_tag *tag;
00631          id3v1_tag *v1;
00632          id3v2_tag *v2;
00633          id3_tag_list *mylist;
00634          int ret = 0;
00635          file_arg arg;
00636          
00637          if(!tag_list) {
00638                  ret |= id3v1_del_tag(fd);
00639                  ret |= id3v2_del_tag(fd, NULL);
00640                  return ret;
00641          }
00642          
00643          while(tag_list)
00644          {
00645                  tag = tag_list->tag;
00646                  if(!tag)
00647                  {
00648                          tag_list = tag_list->next;
00649                          continue;
00650                  }
00651                  
00652                  if(tag->version == 1) 
00653                  {
00654                      id3v1_del_tag(fd);
00655                      ret |= id3v1_add_tag(fd, tag->tag);
00656                  }
00657                  else if(tag->version == 2) 
00658                  {
00659                          int pad = 0;
00660                          id3v2_frame_list *frame_list;
00661                          id3v2_tag *old_v2;
00662                          id3v2_tag *v2 = tag->tag;
00663                          
00664                          /* calculate tag size */
00665                          v2->header->total_tag_size = 10;
00666                          if(v2->header->has_footer) v2->header->total_tag_size += 10;
00667                          if(v2->header->has_extended_header) v2->header->total_tag_size += v2->header->extended_header->size;
00668                          frame_list = v2->frame_list;
00669                          while(frame_list)
00670                          {
00671                                  v2->header->total_tag_size += frame_list->data->data_size + 10;
00672                                  frame_list = frame_list->next;
00673                          }
00674                          
00675                          /* this is where padding handling takes place */
00676                          /* we must get the old tag to see if padding can be used */
00677                          arg.fd = fd;
00678                          old_v2 = id3v2_get_tag(read_file, lseek_file, &arg);
00679                          if(old_v2) {
00680                                  if(v2->header->total_tag_size > old_v2->header->total_tag_size)
00681                                  {
00682                                          /* padding not sufficent */
00683                                          ret |= id3v2_del_tag(fd, old_v2);
00684                                          ret |= id3v2_add_tag(fd, v2, NULL);
00685                                  }
00686                                  else
00687                                  {
00688                                          ret |= id3v2_add_tag(fd, v2, old_v2);
00689                                  }
00690                                  id3v2_free_tag(old_v2);
00691                          } else {
00692                                  ret |= id3v2_add_tag(fd, v2, NULL);
00693                          }
00694                          
00695                  }
00696                  else
00697                  {
00698                          ret |= MP_EVERSION;
00699                  }
00700                  
00701                  tag_list = tag_list->next;
00702          } /* tag list */
00703          
00704          return ret;
00705  }
00706 
00707 int 
00708 mp_del_tags_from_file(const char* filename)
00709 {
00710         int ret, fd;
00711         
00712         if(!filename) return 1;
00713         
00714         fd = open(filename, O_RDWR);
00715         if(fd == -1) return 1;
00716         
00717         ret = mp_del_tags_from_fd(fd);
00718         close(fd);
00719         return ret;
00720 }
00721 
00722 int
00723 mp_del_tags_from_fd(const int fd)
00724 {
00725         int ret = 0;
00726         
00727         ret |= id3v1_del_tag(fd);
00728         ret |= id3v2_del_tag(fd, NULL);
00729         
00730         return ret;
00731 }
00732 
00733 int 
00734 mp_del_tags_by_ver_from_file(const char* filename, const int version)
00735 {
00736         int fd, ret;
00737         
00738         if(!filename) return 1;
00739         
00740         fd = open(filename, O_RDWR);
00741         if(fd == -1) return 1;
00742         
00743         ret = mp_del_tags_by_ver_from_fd(fd, version);
00744         close(fd);
00745         return ret;
00746 }
00747 
00748 int
00749 mp_del_tags_by_ver_from_fd(const int fd, const int version)
00750 {
00751         if(version == 1) return id3v1_del_tag(fd);
00752         else if(version == 2) return id3v2_del_tag(fd, NULL);
00753         else return MP_EVERSION;
00754 }
00755 
00756 
00757 
00758 
00759 /*******************************************************************************************
00760  *                                          Misc
00761  *******************************************************************************************/
00762 
00763  int 
00764  mp_convert_to_v2(id3_tag *tag)
00765  {
00766          id3v1_tag *v1;
00767          id3_tag *tmp;
00768          id3_content* content;
00769          
00770          if(tag->version == 2) return 0;
00771          else if(tag->version == -1) return MP_EVERSION;
00772          
00773          tmp = mp_alloc_tag_with_version(2);
00774          
00775          v1 = (id3v1_tag*)tag->tag;
00776          
00777          content = mp_assemble_text_content(v1->artist, ISO_8859_1);
00778          if(v1->artist) mp_set_content(tmp, MP_ARTIST, content);
00779          
00780          content = mp_assemble_text_content(v1->title, ISO_8859_1);
00781          if(v1->title) mp_set_content(tmp, MP_TITLE, content);
00782          
00783          content = mp_assemble_text_content(v1->album, ISO_8859_1);
00784          if(v1->album) mp_set_content(tmp, MP_ALBUM, content);
00785          
00786          content = mp_assemble_text_content(v1->year, ISO_8859_1);
00787          if(v1->year) mp_set_content(tmp, MP_YEAR, content);
00788          
00789          content = mp_assemble_comment_content(v1->comment, NULL, ISO_8859_1, NULL);
00790          if(v1->comment) mp_set_content(tmp, MP_COMMENT, content);
00791          
00792          if(v1->genre != 0xFF)
00793          {
00794                  char *c = xmallocd(strlen(genre_list[v1->genre]) + 1,
00795                                     "mp_convert_to_v2:c");
00796                  strcpy(c, genre_list[v1->genre]);
00797                  content = mp_assemble_text_content(c, ISO_8859_1);
00798                  mp_set_content(tmp, MP_GENRE, content);
00799          }
00800          if(v1->track > 0)
00801          {
00802                  char *trk = (char *)xmallocd(4, "mp_convert_to_v2:trk");
00803                  snprintf(trk, 3, "%d", v1->track);
00804                  trk[3] = 0;
00805                  content = mp_assemble_text_content(trk, ISO_8859_1);
00806                  mp_set_content(tmp, MP_TRACK, content);
00807          }
00808          
00809          tag->version = 2;
00810          tag->tag = tmp->tag;
00811          
00812          id3v1_free_tag(v1);
00813          xfree(tmp);
00814          
00815          return 0;
00816  }
00817  
00818 int 
00819 mp_convert_to_v1(id3_tag *tag)
00820 {
00821         id3v1_tag *v1;
00822         id3_tag* tmp;
00823         id3_content* content;
00824         id3_text_content* tc;
00825         id3_comment_content* cc;
00826         char* c;
00827         int j, k = 0;
00828         
00829         if(tag->version == 1) return 0;
00830         else if(tag->version == -1) return MP_EVERSION;
00831         
00832         v1 = XMALLOCD0(id3v1_tag, "mp_convert_to_v1:v1");
00833         
00834         content = mp_get_content(tag, MP_ARTIST);
00835         tc = mp_parse_artist(content);
00836         v1->artist = tc->text;
00837         xfree(tc);
00838         mp_free_content(content);
00839         
00840         content = mp_get_content(tag, MP_TITLE);
00841         tc = mp_parse_title(content);
00842         v1->title = tc->text;
00843         xfree(tc);
00844         mp_free_content(content);
00845         
00846         content = mp_get_content(tag, MP_ALBUM);
00847         tc = mp_parse_album(content);
00848         v1->album = tc->text;
00849         xfree(tc);
00850         mp_free_content(content);
00851         
00852         content = mp_get_content(tag, MP_YEAR);
00853         tc = mp_parse_year(content);
00854         v1->year = tc->text;
00855         xfree(tc);
00856         mp_free_content(content);
00857         
00858         content = mp_get_content(tag, MP_COMMENT);
00859         cc = mp_parse_comment(content);
00860         v1->comment = cc->text;
00861         xfree(cc->language);
00862         xfree(cc->short_descr);
00863         xfree(cc);
00864         mp_free_content(content);
00865         
00866         content = mp_get_content(tag, MP_TRACK);
00867         tc = mp_parse_track(content);
00868         c = tc->text;
00869         if(c)
00870         {
00871 #ifdef HAVE_STRTOL
00872                 errno = 0;
00873                 j = strtol(c, (char **)NULL, 10);
00874                 if(errno != ERANGE) v1->track = j;
00875                 else v1->track = 0;
00876 #else
00877                 v1->track = atoi(c);
00878 #endif
00879         }
00880         else v1->track = 0;
00881         mp_free_text_content(tc);
00882         mp_free_content(content);
00883         
00884         content = mp_get_content(tag, MP_GENRE);
00885         tc = mp_parse_genre(content);
00886         c = tc->text;
00887         for(j = 0; c, j < GLL; j++) {
00888                 if(!strcmp(genre_list[j], c))
00889                 {
00890                         v1->genre = j;
00891                         k = 1;
00892                 }
00893         }
00894         if(!c) v1->genre = 0xFF;
00895         mp_free_text_content(tc);
00896         mp_free_content(content);
00897         
00898         id3v1_truncate_tag(v1);
00899         
00900         id3v2_free_tag(tag->tag);
00901         
00902         tag->version = 1;
00903         tag->tag = v1;
00904         
00905         return 0;
00906 }
00907 
00908 int
00909 mp_is_valid_v1_value(int field, char *value)
00910 {
00911         int len = 30;
00912         int j;
00913         
00914         switch(field) {
00915                 case MP_YEAR:
00916                         len = 4;
00917                         break;
00918                         
00919                 case MP_TRACK:
00920 #ifdef HAVE_STRTOL
00921                         errno = 0;
00922                         j = strtol(value, (char **)NULL, 10);
00923                         if(errno != ERANGE) return 1;
00924                         else return 0;
00925 #else
00926                          return 1; /* poor fellow */
00927 #endif
00928 
00929                 case MP_GENRE:
00930                         for(j = 0; j < GLL; j++) {
00931                                 if(!strcmp(genre_list[j], value))
00932                                 {
00933                                         return 1;
00934                                 }
00935                                 return 0;
00936                         }
00937         }
00938 
00939         /* Check string length */
00940         if(strlen(value) > len) return 0;
00941         else return 1;   
00942 }
00943 
00944 
00945 void
00946 mp_free_list(id3_tag_list *list)
00947 {
00948         if(!list) return;
00949         
00950         /* free tag */
00951         if(list->tag) mp_free_tag(list->tag);
00952 
00953         /* free next element */
00954         if(list->next) mp_free_list(list->next);
00955 
00956         /* free this element */
00957         xfree(list); 
00958 }
00959 
00960 id3_tag*
00961 mp_alloc_tag(void)
00962 {
00963         /* The tags initialized version makes a different. Generally spoken, we 
00964         like to make id3v1 tags if possible and therefor set the version to 1
00965         here. This matters in mp_set_content(). */
00966         return mp_alloc_tag_with_version(1);
00967 }
00968 
00969 id3_tag*
00970 mp_alloc_tag_with_version(int v)
00971 {
00972         id3_tag* ret;
00973         
00974         if(v != 1 && v != 2) return NULL;
00975         
00976         ret = XMALLOCD(id3_tag, "mp_alloc_tag_with_version:ret");
00977         ret->version = v;
00978         if(v == 1)
00979         {
00980                 ret->tag = XMALLOCD0(id3v1_tag, "mp_alloc_tag_with_version:ret->tag");
00981                 ((id3v1_tag*)ret->tag)->genre = 0xFF;
00982         }
00983         else
00984         {
00985                 id3v2_tag *v2;
00986                 /* XXX */
00987                 ret->tag = XMALLOCD0(id3v2_tag, "mp_alloc_tag_with_version:ret->tag");
00988                 v2 = (id3v2_tag*)ret->tag;
00989                 v2->header = XMALLOCD0(id3v2_header, "mp_alloc_tag_with_version:v2->header");
00990 //if ID3VERSION == "2.4"
00991                 v2->header->version_minor = 4;
00992 //else
00993                 v2->header->version_minor = 3;
00994 //endif
00995                 v2->header->version_revision = 0;
00996                 v2->header->unsyncronization = 1;
00997                 v2->header->has_extended_header = 0;
00998                 v2->header->is_experimental = 1;
00999                 v2->header->has_footer = 0;
01000                 v2->header->flags = 0;
01001                 v2->header->total_tag_size = 0;
01002                 v2->header->extended_header = NULL;
01003                 v2->frame_list = NULL;
01004         }
01005         return ret;
01006 }
01007 
01008 void 
01009 mp_free_tag(id3_tag *tag)
01010 {
01011         if(!tag) return;
01012         
01013         if(tag->version == 1)
01014         {
01015                 id3v1_free_tag(tag->tag);
01016         }
01017         else if(tag->version == 2)
01018         {
01019                 id3v2_free_tag(tag->tag);
01020         }
01021         xfree(tag);
01022 }
01023 
01024 void
01025 mp_free_content(id3_content *content)
01026 {
01027         if(!content) return;
01028         xfree(content->data);
01029         xfree(content);
01030 }
01031 
01032 void
01033 mp_free_text_content(id3_text_content *content)
01034 {
01035         if(!content) return;
01036         xfree(content->text);
01037         xfree(content);
01038 }
01039 
01040 void 
01041 mp_free_comment_content(id3_comment_content *content)
01042 {
01043         if(!content) return;
01044         xfree(content->language);
01045         xfree(content->short_descr);
01046         xfree(content->text);
01047         xfree(content);
01048 }
01049 
01050