fixes based on a pass over everything with autoconf/automake
[platform/upstream/flac.git] / src / plugin_xmms / id3_tag.c
1 /* libxmms-flac - XMMS FLAC input plugin
2  * Copyright (C) 2002  Daisuke Shimamura
3  *
4  * Almost from id3_tag.c - 2001/02/16
5  *  EasyTAG - Tag editor for MP3 and OGG files
6  *  Copyright (C) 2001-2002  Jerome Couderc <j.couderc@ifrance.com>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  */
22
23 #include <id3.h>
24 #include <gtk/gtk.h>
25 #include <stdio.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <ctype.h>
29 #include <unistd.h>
30
31 #include "configure.h"
32 #include "genres.h"
33 #include "charset.h"
34 #include "mylocale.h"
35 #include "id3_tag.h"
36
37 /****************
38  * Declarations *
39  ****************/
40 #define ID3V2_MAX_STRING_LEN 4096
41 #define NUMBER_TRACK_FORMATED 1
42
43 /**************
44  * Prototypes *
45  **************/
46 static size_t ID3Tag_Link_1       (ID3Tag *id3tag, const char *filename);
47 static size_t ID3Field_GetASCII_1 (const ID3Field *field, char *buffer, size_t maxChars, index_t itemNum);
48
49 static gchar  *Id3tag_Genre_To_String (unsigned char genre_code);
50 static void Strip_String (gchar *string);
51
52 /*************
53  * Functions *
54  *************/
55 /*
56  * Read id3v1.x / id3v2 tag and load data into the File_Tag structure using id3lib functions.
57  * Returns TRUE on success, else FALSE.
58  * If a tag entry exists (ex: title), we allocate memory, else value stays to NULL
59  */
60 gboolean Id3tag_Read_File_Tag (gchar *filename, File_Tag *FileTag)
61 {
62     FILE *file;
63     ID3Tag *id3_tag = NULL;    /* Tag defined by the id3lib */
64     gchar *string, *string1, *string2;
65     gboolean USE_CHARACTER_SET_TRANSLATION;
66
67     USE_CHARACTER_SET_TRANSLATION = flac_cfg.convert_char_set;
68
69     if (!filename || !FileTag)
70         return FALSE;
71
72     if ( (file=fopen(filename,"r"))==NULL )
73     {
74         g_print(_("ERROR while opening file: '%s' (%s).\n\a"),filename,g_strerror(errno));
75         return FALSE;
76     }
77     fclose(file); // We close it cause id3lib opens/closes file itself
78
79
80     /* Get data from tag */
81     if ( (id3_tag = ID3Tag_New()) )
82     {
83         ID3Frame *id3_frame;
84         ID3Field *id3_field;
85         luint frm_size;
86         luint num_chars;
87         guint field_num = 0; // First field
88
89         /* Link the file to the tag */
90         frm_size = ID3Tag_Link_1(id3_tag,filename);
91
92         string = g_malloc(ID3V2_MAX_STRING_LEN+1);
93
94         /*********
95          * Title *
96          *********/
97         if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_TITLE)) )
98         {
99             if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_TEXT)) )
100             {
101                 // Note: if 'num_chars' is equal to 0, then the field is empty or corrupted!
102                 if ( (num_chars=ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,field_num)) > 0
103                      && string != NULL )
104                 {
105                     if (USE_CHARACTER_SET_TRANSLATION)
106                     {
107                         string1 = convert_from_file_to_user(string);
108                         Strip_String(string1);
109                         FileTag->title = g_strdup(string1);
110                         g_free(string1);
111                     }else
112                     {
113                         Strip_String(string);
114                         FileTag->title = g_strdup(string);
115                     }
116                 }
117             }
118         }
119
120
121         /**********
122          * Artist *
123          **********/
124         if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_LEADARTIST)) )
125         {
126             if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_TEXT)) )
127             {
128                 if ( (num_chars=ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,field_num)) > 0
129                      && string != NULL )
130                 {
131                     if (USE_CHARACTER_SET_TRANSLATION)
132                     {
133                         string1 = convert_from_file_to_user(string);
134                         Strip_String(string1);
135                         FileTag->artist = g_strdup(string1);
136                         g_free(string1);
137                     }else
138                     {
139                         Strip_String(string);
140                         FileTag->artist = g_strdup(string);
141                     }
142                 }
143             }
144         }
145
146
147         /*********
148          * Album *
149          *********/
150         if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_ALBUM)) )
151         {
152             if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_TEXT)) )
153             {
154                 if ( (num_chars=ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,field_num)) > 0
155                      && string != NULL )
156                 {
157                     if (USE_CHARACTER_SET_TRANSLATION)
158                     {
159                         string1 = convert_from_file_to_user(string);
160                         Strip_String(string1);
161                         FileTag->album = g_strdup(string1);
162                         g_free(string1);
163                     }else
164                     {
165                         Strip_String(string);
166                         FileTag->album = g_strdup(string);
167                     }
168                 }
169             }
170         }
171
172
173         /********
174          * Year *
175          ********/
176         if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_YEAR)) )
177         {
178             if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_TEXT)) )
179             {
180                 if ( (num_chars=ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,field_num)) > 0
181                      && string != NULL )
182                 {
183                     gchar *tmp_str;
184
185                     Strip_String(string);
186
187                     /* Fix for id3lib 3.7.x: if the id3v1.x tag was filled with spaces
188                      * instead of zeroes, then the year field contains garbages! */
189                     tmp_str = string;
190                     while (isdigit(*tmp_str)) tmp_str++;
191                     *tmp_str = 0;
192                     /* End of fix for id3lib 3.7.x */
193
194                     if (USE_CHARACTER_SET_TRANSLATION)
195                     {
196                         string1 = convert_from_file_to_user(string);
197                         Strip_String(string1);
198                         FileTag->year = g_strdup(string1);
199                         g_free(string1);
200                     }else
201                     {
202                         Strip_String(string);
203                         FileTag->year = g_strdup(string);
204                     }
205                 }
206             }
207         }
208
209
210         /*************************
211          * Track and Total Track *
212          *************************/
213         if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_TRACKNUM)) )
214         {
215             if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_TEXT)) )
216             {
217                 if ( (num_chars=ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,field_num)) > 0
218                      && string != NULL )
219                 {
220                     
221                     Strip_String(string);
222
223                     if (USE_CHARACTER_SET_TRANSLATION)
224                     {
225                         string1 = convert_from_file_to_user(string);
226                         string2 = strchr(string1,'/');
227                         if (NUMBER_TRACK_FORMATED)
228                         {
229                             if (string2)
230                             {
231                                 FileTag->track_total = g_strdup_printf("%.2d",atoi(string2+1)); // Just to have numbers like this : '01', '05', '12', ...
232                                 *string2 = '\0';
233                             }
234                             FileTag->track = g_strdup_printf("%.2d",atoi(string1)); // Just to have numbers like this : '01', '05', '12', ...
235                         }else
236                         {
237                             if (string2)
238                             {
239                                 FileTag->track_total = g_strdup(string2+1);
240                                 *string2 = '\0';
241                             }
242                             FileTag->track = g_strdup(string1);
243                         }
244                         g_free(string1);
245                     }else
246                     {
247                         string2 = strchr(string,'/');
248                         if (NUMBER_TRACK_FORMATED)
249                         {
250                             if (string2)
251                             {
252                                 FileTag->track_total = g_strdup_printf("%.2d",atoi(string2+1)); // Just to have numbers like this : '01', '05', '12', ...
253                                 *string2 = '\0';
254                             }
255                             FileTag->track = g_strdup_printf("%.2d",atoi(string)); // Just to have numbers like this : '01', '05', '12', ...
256                         }else
257                             {
258                             if (string2)
259                             {
260                                 FileTag->track_total = g_strdup(string2+1);
261                                 *string2 = '\0';
262                             }
263                             FileTag->track = g_strdup(string);
264                         }
265                     }
266                 }
267             }
268         }
269
270
271         /*********
272          * Genre *
273          *********/
274         if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_CONTENTTYPE)) )
275         {
276             if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_TEXT)) )
277             {
278                 /*
279                  * We manipulate only the name of the genre
280                  */
281                 if ( (num_chars=ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,field_num)) > 0
282                      && string != NULL )
283                 {
284                     gchar *tmp;
285
286                     Strip_String(string);
287
288                     if ( (string[0]=='(') && (tmp=strchr(string,')')) && (strlen((tmp+1))>0) )
289                     {
290     
291                         /* Convert a genre written as '(3)Dance' into 'Dance' */
292                         if (USE_CHARACTER_SET_TRANSLATION)
293                         {
294                             string1 = convert_from_file_to_user(tmp+1);
295                             FileTag->genre = g_strdup(string1);
296                             g_free(string1);
297                         }else
298                         {
299                             FileTag->genre = g_strdup(tmp+1);
300                         }
301
302                     }else if ( (string[0]=='(') && (tmp=strchr(string,')')) )
303                     {
304     
305                         /* Convert a genre written as '(3)' into 'Dance' */
306                         *tmp = 0;
307                         if (USE_CHARACTER_SET_TRANSLATION)
308                         {
309                             string1 = convert_from_file_to_user(Id3tag_Genre_To_String(atoi(string+1)));
310                             FileTag->genre = g_strdup(string1);
311                             g_free(string1);
312                         }else
313                         {
314                             FileTag->genre = g_strdup(Id3tag_Genre_To_String(atoi(string+1)));
315                         }
316
317                     }else
318                     {
319
320                         /* Genre is already written as 'Dance' */
321                             if (USE_CHARACTER_SET_TRANSLATION)
322                         {
323                             string1 = convert_from_file_to_user(string);
324                             FileTag->genre = g_strdup(string1);
325                             g_free(string1);
326                         }else
327                         {
328                             FileTag->genre = g_strdup(string);
329                         }
330
331                     }
332                 }
333             }
334         }
335
336
337         /***********
338          * Comment *
339          ***********/
340         if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_COMMENT)) )
341         {
342             if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_TEXT)) )
343             {
344                 if ( (num_chars=ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,field_num)) > 0
345                      && string != NULL )
346                 {
347                     if (USE_CHARACTER_SET_TRANSLATION)
348                     {
349                         string1 = convert_from_file_to_user(string);
350                         Strip_String(string1);
351                         FileTag->comment = g_strdup(string1);
352                         g_free(string1);
353                     }else
354                     {
355                         Strip_String(string);
356                         FileTag->comment = g_strdup(string);
357                     }
358                 }
359             }
360             /*if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_DESCRIPTION)) )
361             {
362                 gchar *comment1 = g_malloc0(MAX_STRING_LEN+1);
363                 num_chars = ID3Field_GetASCII(id3_field,comment1,MAX_STRING_LEN,Item_Num);
364                 g_free(comment1);
365             }
366             if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_LANGUAGE)) )
367             {
368                 gchar *comment2 = g_malloc0(MAX_STRING_LEN+1);
369                 num_chars = ID3Field_GetASCII(id3_field,comment2,MAX_STRING_LEN,Item_Num);
370                 g_free(comment2);
371             }*/
372         }
373         g_free(string);
374
375         /* Free allocated data */
376         ID3Tag_Delete(id3_tag);
377     }
378
379     return TRUE;
380 }
381
382 void Initialize_File_Tag_Item (File_Tag *FileTag)
383 {
384     if (FileTag)
385     {
386         FileTag->key         = 0;
387         FileTag->saved       = FALSE;    
388         FileTag->title       = NULL;
389         FileTag->artist      = NULL;
390         FileTag->album       = NULL;
391         FileTag->track       = NULL;
392         FileTag->track_total = NULL;
393         FileTag->year        = NULL;
394         FileTag->genre       = NULL;
395         FileTag->comment     = NULL;
396     }
397 }
398
399 /*
400  * Frees a File_Tag item.
401  */
402 gboolean Free_File_Tag_Item (File_Tag *FileTag)
403 {
404     if (!FileTag) return FALSE;
405
406     if (FileTag->title)       g_free(FileTag->title);
407     if (FileTag->artist)      g_free(FileTag->artist);
408     if (FileTag->album)       g_free(FileTag->album);
409     if (FileTag->year)        g_free(FileTag->year);
410     if (FileTag->track)       g_free(FileTag->track);
411     if (FileTag->track_total) g_free(FileTag->track_total);
412     if (FileTag->genre)       g_free(FileTag->genre);
413     if (FileTag->comment)     g_free(FileTag->comment);
414
415     return TRUE;
416 }
417
418 /*
419  * Returns the name of a genre code if found
420  * Three states for genre code :
421  *    - defined (0 to GENRE_MAX)
422  *    - undefined/unknown (GENRE_MAX+1 to ID3_INVALID_GENRE-1)
423  *    - invalid (>ID3_INVALID_GENRE)
424  */
425 static gchar *Id3tag_Genre_To_String (unsigned char genre_code)
426 {
427     if (genre_code>=ID3_INVALID_GENRE)    /* empty */
428         return "";
429     else if (genre_code>GENRE_MAX)        /* unknown tag */
430         return "Unknown";
431     else                                  /* known tag */
432         return id3_genres[genre_code];
433 }
434
435
436
437 /*
438  * As the ID3Tag_Link function of id3lib-3.8.0pre2 returns the ID3v1 tags
439  * when a file has both ID3v1 and ID3v2 tags, we first try to explicitely
440  * get the ID3v2 tags with ID3Tag_LinkWithFlags and, if we cannot get them,
441  * fall back to the ID3v1 tags.
442  * (Written by Holger Schemel).
443  */
444 static size_t ID3Tag_Link_1 (ID3Tag *id3tag, const char *filename)
445 {
446     size_t offset;
447
448 #   if ( (ID3LIB_MAJOR >= 3) && (ID3LIB_MINOR >= 8)  )
449         /* First, try to get the ID3v2 tags */
450         offset = ID3Tag_LinkWithFlags(id3tag,filename,ID3TT_ID3V2);
451         if (offset == 0)
452         {
453             /* No ID3v2 tags available => try to get the ID3v1 tags */
454             offset = ID3Tag_LinkWithFlags(id3tag,filename,ID3TT_ID3V1);
455         }
456 #   else
457         /* Function 'ID3Tag_LinkWithFlags' is not defined up to id3lib-.3.7.13 */
458         offset = ID3Tag_Link(id3tag,filename);
459 #   endif
460     //g_print("ID3 TAG SIZE: %d\t%s\n",offset,g_basename(filename));
461     return offset;
462 }
463
464
465 /*
466  * As the ID3Field_GetASCII function differs with the version of id3lib, we must redefine it.
467  */
468 static size_t ID3Field_GetASCII_1(const ID3Field *field, char *buffer, size_t maxChars, index_t itemNum)
469 {
470
471     /* Defined by id3lib:   ID3LIB_MAJOR_VERSION, ID3LIB_MINOR_VERSION, ID3LIB_PATCH_VERSION
472      * Defined by autoconf: ID3LIB_MAJOR,         ID3LIB_MINOR,         ID3LIB_PATCH
473      *
474      * <= 3.7.12 : first item num is 1 for ID3Field_GetASCII
475      *  = 3.7.13 : first item num is 0 for ID3Field_GetASCII
476      * >= 3.8.0  : doesn't need item num for ID3Field_GetASCII
477      */
478      //g_print("id3lib version: %d.%d.%d\n",ID3LIB_MAJOR,ID3LIB_MINOR,ID3LIB_PATCH);
479 #    if (ID3LIB_MAJOR >= 3)
480          // (>= 3.x.x)
481 #        if (ID3LIB_MINOR <= 7)
482              // (3.0.0 to 3.7.x)
483 #            if (ID3LIB_PATCH >= 13)
484                  // (>= 3.7.13)
485                  return ID3Field_GetASCII(field,buffer,maxChars,itemNum);
486 #            else
487                  return ID3Field_GetASCII(field,buffer,maxChars,itemNum+1);
488 #            endif
489 #        else
490              // (>= to 3.8.0)
491              //return ID3Field_GetASCII(field,buffer,maxChars);
492              return ID3Field_GetASCIIItem(field,buffer,maxChars,itemNum);
493 #        endif
494 #    else
495          // Not tested (< 3.x.x)
496          return ID3Field_GetASCII(field,buffer,maxChars,itemNum+1);
497 #    endif
498 }
499
500 /*
501  * Delete spaces at the end and the beginning of the string 
502  */
503 static void Strip_String (gchar *string)
504 {
505     if (!string) return;
506     string = g_strstrip(string);
507 }
508