1 /* Time-stamp: <2006-05-01 15:04:54 jcs>
2 |
3 |  Copyright (C) 2002-2005 Jorg Schuler <jcsjcs at users sourceforge net>
4 |  Part of the gtkpod project.
5 |
6 |  URL: http://www.gtkpod.org/
7 |  URL: http://gtkpod.sourceforge.net/
8 |
9 |  This program is free software; you can redistribute it and/or modify
10 |  it under the terms of the GNU General Public License as published by
11 |  the Free Software Foundation; either version 2 of the License, or
12 |  (at your option) any later version.
13 |
14 |  This program is distributed in the hope that it will be useful,
15 |  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 |  GNU General Public License for more details.
18 |
19 |  You should have received a copy of the GNU General Public License
20 |  along with this program; if not, write to the Free Software
21 |  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 |
23 |  iTunes and iPod are trademarks of Apple
24 |
25 |  This product is not supported/written/published by Apple!
26 |
27 |  $Id$
28 */
29 
30 #ifdef HAVE_CONFIG_H
31 #  include <config.h>
32 #endif
33 
34 #include <string.h>
35 #include <gtk/gtk.h>
36 #include "prefs.h"
37 #include "charset.h"
38 #include "misc.h"
39 
40 /* If Japanese auto-conversion is being used, this variable is being
41    set with each call of charset_to_utf8(). You can get a copy of its
42    value by calling charset_get_auto().
43    This variable will only be reset by calling charset_reset_auto(). */
44 static gchar *auto_charset = NULL;
45 
46 typedef struct {
47 	const gchar *descr;
48 	const gchar *name;
49 } CharsetInfo;
50 
51 
52 static const CharsetInfo charset_info[] = {
53     {N_("Arabic (IBM-864)"),                  "IBM864"        },
54     {N_("Arabic (ISO-8859-6)"),               "ISO-8859-6"    },
55     {N_("Arabic (Windows-1256)"),             "windows-1256"  },
56     {N_("Baltic (ISO-8859-13)"),              "ISO-8859-13"   },
57     {N_("Baltic (ISO-8859-4)"),               "ISO-8859-4"    },
58     {N_("Baltic (Windows-1257)"),             "windows-1257"  },
59     {N_("Celtic (ISO-8859-14)"),              "ISO-8859-14"   },
60     {N_("Central European (IBM-852)"),        "IBM852"        },
61     {N_("Central European (ISO-8859-2)"),     "ISO-8859-2"    },
62     {N_("Central European (Windows-1250)"),   "windows-1250"  },
63     {N_("Chinese Simplified (GB18030)"),      "gb18030"       },
64     {N_("Chinese Simplified (GB2312)"),       "GB2312"        },
65     {N_("Chinese Traditional (Big5)"),        "Big5"          },
66     {N_("Chinese Traditional (Big5-HKSCS)"),  "Big5-HKSCS"    },
67     {N_("Cyrillic (IBM-855)"),                "IBM855"        },
68     {N_("Cyrillic (ISO-8859-5)"),             "ISO-8859-5"    },
69     {N_("Cyrillic (ISO-IR-111)"),             "ISO-IR-111"    },
70     {N_("Cyrillic (KOI8-R)"),                 "KOI8-R"        },
71     {N_("Cyrillic (Windows-1251)"),           "windows-1251"  },
72     {N_("Cyrillic/Russian (CP-866)"),         "IBM866"        },
73     {N_("Cyrillic/Ukrainian (KOI8-U)"),       "KOI8-U"        },
74     {N_("English (US-ASCII)"),                "us-ascii"      },
75     {N_("Greek (ISO-8859-7)"),                "ISO-8859-7"    },
76     {N_("Greek (Windows-1253)"),              "windows-1253"  },
77     {N_("Hebrew (IBM-862)"),                  "IBM862"        },
78     {N_("Hebrew (Windows-1255)"),             "windows-1255"  },
79     {N_("Japanese (automatic detection)"),    GTKPOD_JAPAN_AUTOMATIC},
80     {N_("Japanese (EUC-JP)"),                 "EUC-JP"        },
81     {N_("Japanese (ISO-2022-JP)"),            "ISO-2022-JP"   },
82     {N_("Japanese (Shift_JIS)"),              "Shift_JIS"     },
83     {N_("Korean (EUC-KR)"),                   "EUC-KR"        },
84     {N_("Nordic (ISO-8859-10)"),              "ISO-8859-10"   },
85     {N_("South European (ISO-8859-3)"),       "ISO-8859-3"    },
86     {N_("Thai (TIS-620)"),                    "TIS-620"       },
87     {N_("Turkish (IBM-857)"),                 "IBM857"        },
88     {N_("Turkish (ISO-8859-9)"),              "ISO-8859-9"    },
89     {N_("Turkish (Windows-1254)"),            "windows-1254"  },
90     {N_("Unicode (UTF-7)"),                   "UTF-7"         },
91     {N_("Unicode (UTF-8)"),                   "UTF-8"         },
92     {N_("Unicode (UTF-16BE)"),                "UTF-16BE"      },
93     {N_("Unicode (UTF-16LE)"),                "UTF-16LE"      },
94     {N_("Unicode (UTF-32BE)"),                "UTF-32BE"      },
95     {N_("Unicode (UTF-32LE)"),                "UTF-32LE"      },
96     {N_("Vietnamese (VISCII)"),               "VISCII"        },
97     {N_("Vietnamese (Windows-1258)"),         "windows-1258"  },
98     {N_("Visual Hebrew (ISO-8859-8)"),        "ISO-8859-8"    },
99     {N_("Western (IBM-850)"),                 "IBM850"        },
100     {N_("Western (ISO-8859-1)"),              "ISO-8859-1"    },
101     {N_("Western (ISO-8859-15)"),             "ISO-8859-15"   },
102     {N_("Western (Windows-1252)"),            "windows-1252"  },
103     /*
104      * From this point, character sets aren't supported by iconv
105      */
106 /*    {N_("Arabic (IBM-864-I)"),                "IBM864i"              },
107     {N_("Arabic (ISO-8859-6-E)"),             "ISO-8859-6-E"         },
108     {N_("Arabic (ISO-8859-6-I)"),             "ISO-8859-6-I"         },
109     {N_("Arabic (MacArabic)"),                "x-mac-arabic"         },
110     {N_("Armenian (ARMSCII-8)"),              "armscii-8"            },
111     {N_("Central European (MacCE)"),          "x-mac-ce"             },
112     {N_("Chinese Simplified (GBK)"),          "x-gbk"                },
113     {N_("Chinese Simplified (HZ)"),           "HZ-GB-2312"           },
114     {N_("Chinese Traditional (EUC-TW)"),      "x-euc-tw"             },
115     {N_("Croatian (MacCroatian)"),            "x-mac-croatian"       },
116     {N_("Cyrillic (MacCyrillic)"),            "x-mac-cyrillic"       },
117     {N_("Cyrillic/Ukrainian (MacUkrainian)"), "x-mac-ukrainian"      },
118     {N_("Farsi (MacFarsi)"),                  "x-mac-farsi"},
119     {N_("Greek (MacGreek)"),                  "x-mac-greek"          },
120     {N_("Gujarati (MacGujarati)"),            "x-mac-gujarati"       },
121     {N_("Gurmukhi (MacGurmukhi)"),            "x-mac-gurmukhi"       },
122     {N_("Hebrew (ISO-8859-8-E)"),             "ISO-8859-8-E"         },
123     {N_("Hebrew (ISO-8859-8-I)"),             "ISO-8859-8-I"         },
124     {N_("Hebrew (MacHebrew)"),                "x-mac-hebrew"         },
125     {N_("Hindi (MacDevanagari)"),             "x-mac-devanagari"     },
126     {N_("Icelandic (MacIcelandic)"),          "x-mac-icelandic"      },
127     {N_("Korean (JOHAB)"),                    "x-johab"              },
128     {N_("Korean (UHC)"),                      "x-windows-949"        },
129     {N_("Romanian (MacRomanian)"),            "x-mac-romanian"       },
130     {N_("Turkish (MacTurkish)"),              "x-mac-turkish"        },
131     {N_("User Defined"),                      "x-user-defined"       },
132     {N_("Vietnamese (TCVN)"),                 "x-viet-tcvn5712"      },
133     {N_("Vietnamese (VPS)"),                  "x-viet-vps"           },
134     {N_("Western (MacRoman)"),                "x-mac-roman"          },
135     // charsets whithout posibly translatable names
136     {"T61.8bit",                              "T61.8bit"             },
137     {"x-imap4-modified-utf7",                 "x-imap4-modified-utf7"},
138     {"x-u-escaped",                           "x-u-escaped"          },
139     {"windows-936",                           "windows-936"          }
140 */
141     {NULL, NULL}
142 };
143 
144 
145 
146 /* Sets up the charsets to choose from in the "combo". It presets the
147    charset stored in cfg->charset (or "System Charset" if none is set
148    there
149 
150    DEPRECATED - use charset_init_combo_box with GtkComboBox
151 */
charset_init_combo(GtkCombo * combo)152 void charset_init_combo (GtkCombo *combo)
153 {
154     gchar *current_charset;
155     gchar *description;
156     const CharsetInfo *ci;
157     static GList *charsets = NULL; /* list with choices -- takes a while to
158 				     * initialize, so we only do it once */
159 
160     current_charset = prefs_get_string("charset");
161     if ((current_charset == NULL) || (strlen (current_charset) == 0))
162     {
163 	description = g_strdup (_("System Charset"));
164     }
165     else
166     {
167 	description = charset_to_description (current_charset);
168     }
169     if (charsets == NULL)
170     { /* set up list with charsets */
171 	FILE *fp;
172 
173 	charsets = g_list_append (charsets, _("System Charset"));
174 	/* now add all the charset descriptions in the list above */
175 	ci=charset_info;
176 	while (ci->descr != NULL)
177 	{
178 	    charsets = g_list_append (charsets, _(ci->descr));
179 	    ++ci;
180 	}
181 	/* let's add all available charsets returned by "iconv -l" */
182 	/* The code assumes that "iconv -l" returns a list with the
183 	   name of one charset in each line, each valid line being
184 	   terminated by "//".  */
185 	fp = popen ("iconv -l", "r");
186 	if (fp)
187 	{
188 	    gchar buf[PATH_MAX];
189 	    /* read one line of output at a time */
190 	    while (fgets (buf, PATH_MAX, fp))
191 	    {
192 		/* only consider lines ending on "//" */
193 		gchar *bufp = g_strrstr (buf, "//\n");
194 		if (bufp)
195 		{  /* add everything before "//" to our charset list */
196 		    gchar *bufpp = buf;
197 		    *bufp = 0;  /* shorten string */
198 		    while ((*bufpp == ' ') || (*bufpp == 0x09))
199 			++bufpp; /* skip whitespace */
200 		    if (*bufpp)
201 			charsets = g_list_append (charsets, g_strdup (bufpp));
202 		}
203 	    }
204 	    pclose (fp);
205 	}
206     }
207     /* set pull down items */
208     gtk_combo_set_popdown_strings (GTK_COMBO (combo), charsets);
209     /* set standard entry */
210     gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (combo)->entry), description);
211     g_free (description);
212     g_free(current_charset);
213 }
214 
215 /* Sets up the charsets to choose from in the "combo". It presets the
216    charset stored in cfg->charset (or "System Charset" if none is set
217    there */
charset_init_combo_box(GtkComboBox * combo)218 void charset_init_combo_box (GtkComboBox *combo)
219 {
220     gchar *current_charset;
221     gchar *description;
222     const CharsetInfo *ci;
223 	GtkCellRenderer *renderer;
224 	GtkTreeIter use_iter;
225     static GtkListStore *charsets = NULL; /* list with choices -- takes a while to
226 				     * initialize, so we only do it once */
227 
228     current_charset = prefs_get_string("charset");
229 
230     if ((current_charset == NULL) || (strlen (current_charset) == 0))
231     {
232 		description = g_strdup (_("System Charset"));
233     }
234     else
235     {
236 		description = charset_to_description (current_charset);
237     }
238 
239     if (charsets == NULL)
240     { /* set up list with charsets */
241 		FILE *fp;
242 		GtkTreeIter iter;
243 
244 		charsets = gtk_list_store_new (1, G_TYPE_STRING);
245 		/* now add all the charset descriptions in the list above */
246 
247 		gtk_list_store_append (charsets, &iter);
248 		gtk_list_store_set (charsets, &iter, 0, _("System Charset"), -1);
249 
250 		for (ci = charset_info; ci->descr; ci++)
251 		{
252 			gtk_list_store_append (charsets, &iter);
253 			gtk_list_store_set (charsets, &iter, 0, _(ci->descr), -1);
254 		}
255 
256 		/* let's add all available charsets returned by "iconv -l" */
257 		/* The code assumes that "iconv -l" returns a list with the
258 		   name of one charset in each line, each valid line being
259 		   terminated by "//".  */
260 		fp = popen ("iconv -l", "r");
261 
262 		if (fp)
263 		{
264 			gchar buf[PATH_MAX];
265 
266 			/* read one line of output at a time */
267 			while (fgets (buf, PATH_MAX, fp))
268 			{
269 				/* only consider lines ending on "//" */
270 				gchar *bufp = g_strrstr (buf, "//\n");
271 
272 				if (bufp)
273 				{
274 					/* add everything before "//" to our charset list */
275 					gchar *bufpp = buf;
276 					*bufp = 0;  /* shorten string */
277 
278 					while ((*bufpp == ' ') || (*bufpp == 0x09))
279 						bufpp++; /* skip whitespace */
280 
281 					if (*bufpp)
282 					{
283 						gtk_list_store_append (charsets, &iter);
284 						gtk_list_store_set (charsets, &iter, 0, bufpp, -1);
285 
286 					}
287 				}
288 			}
289 
290 			pclose (fp);
291 		}
292     }
293     /* set pull down items */
294 	renderer = gtk_cell_renderer_text_new ();
295 
296     gtk_combo_box_set_model (GTK_COMBO_BOX (combo), GTK_TREE_MODEL (charsets));
297 	gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo));
298 	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE);
299 	gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), renderer, "text", 0);
300 
301 	for (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (charsets), &use_iter);
302 		 gtk_list_store_iter_is_valid (charsets, &use_iter);
303 		 gtk_tree_model_iter_next (GTK_TREE_MODEL (charsets), &use_iter))
304 	{
305 		gchar *cur_desc;
306 		gtk_tree_model_get (GTK_TREE_MODEL (charsets), &use_iter, 0, &cur_desc, -1);
307 
308 		if(!g_utf8_collate(description, cur_desc))
309 		{
310 			g_free (cur_desc);
311 			break;
312 		}
313 
314 		g_free (cur_desc);
315 	}
316 
317 	gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &use_iter);
318     g_free (description);
319     g_free (current_charset);
320 }
321 
322 /* returns the charset name belonging to the description "descr"
323  * chosen from the combo. Return "NULL" when it could not be found, or
324  * if it is the System Default Charset (locale). You must g_free
325  * the charset received (unlike g_get_charset ()) */
charset_from_description(gchar * descr)326 gchar *charset_from_description (gchar *descr)
327 {
328     const CharsetInfo *ci;
329 
330     if (!descr) return NULL; /* sanity! */
331     /* check for "System Charset" and return NULL */
332     if (g_utf8_collate (descr, _("System Charset")) == 0)   return NULL;
333     /* check if description matches one of the descriptions in
334      * charset_info[], and if so, return the charset name */
335     ci = charset_info;
336     while (ci->descr != NULL)
337     {
338 	if (g_utf8_collate (descr, _(ci->descr)) == 0)
339 	{
340 	    return g_strdup (ci->name);
341 	}
342 	++ci;
343     }
344     /* OK, it was not in the charset_info[] list. Therefore it must be
345      * from the "iconv -l" list. We just return a copy of it */
346     return g_strdup (descr);
347 }
348 
349 
350 /* Returns the description belonging to the charset "charset"
351  * Returns the description (if found) or just a copy of "charset"
352  * You must g_free the charset received */
charset_to_description(gchar * charset)353 gchar *charset_to_description (gchar *charset)
354 {
355     const CharsetInfo *ci;
356 
357     if (!charset) return NULL; /* sanity! */
358     /* check if charset matches one of the charsets in
359      * charset_info[], and if so, return the description */
360     ci = charset_info;
361     while (ci->descr != NULL)
362     {
363 	if (compare_string_case_insensitive (charset, ci->name) == 0)
364 	{
365 	    return g_strdup (_(ci->descr));
366 	}
367 	++ci;
368     }
369     /* OK, it was not in the charset_info[] list. Therefore it must be
370      * from the "iconv -l" list. We just return a copy of it */
371     return g_strdup (charset);
372 }
373 
374 
375 /* code for automatic detection of Japanese char-subset donated by
376    Hiroshi Kawashima */
charset_check_k_code(const gchar * p2)377 static const gchar *charset_check_k_code (const gchar *p2)
378 {
379     static const char* charsets[] = {
380 	    "UTF-8",
381 	    "EUC-JP",
382 	    "CP932",
383 	    "ISO-2022-JP",
384 	    NULL
385     };
386     int i;
387     gchar *ret;
388     gssize len;
389 
390     if (p2 == NULL) return NULL;
391 
392     len = strlen ((gchar*)p2);
393     for (i=0; charsets[i]; i++) {
394       ret = g_convert ((gchar*)p2,            /* string to convert */
395 		       len,                  /* length of string  */
396 		       "UTF-8",              /* to_codeset        */
397 		       charsets[i],          /* from_codeset      */
398 		       NULL,                 /* *bytes_read       */
399 		       NULL,                 /* *bytes_written    */
400 		       NULL);                /* GError **error    */
401       if (ret != NULL) {
402         g_free(ret);
403         return charsets[i];
404       }
405     }
406     return (NULL);
407 }
408 
409 /* same as check_k_code, but defaults to "UTF-8" if no match is found */
charset_check_k_code_with_default(const guchar * p)410 static const gchar *charset_check_k_code_with_default (const guchar *p)
411 {
412     const gchar *result=NULL;
413 
414     if (p)       result = charset_check_k_code (p);
415     if (!result) result = "UTF-8";
416     return result;
417 }
418 
419 /* return the charset actually used for the "auto detection"
420  * feature. So far only Japanese Auto Detecion is implemented */
charset_check_auto(const gchar * str)421 static gchar *charset_check_auto (const gchar *str)
422 {
423     gchar *charset;
424     gchar *result;
425 
426     if (str == NULL) return NULL; /* sanity */
427 
428     charset = prefs_get_string("charset");
429 
430     if (charset && (strcmp (charset, GTKPOD_JAPAN_AUTOMATIC) == 0))
431 	result = g_strdup(charset_check_k_code (str));
432     else
433 	result = NULL;
434 
435     g_free(charset);
436     return result;
437 }
438 
439 /* See description at the definition of gchar *auto_charset; for
440    details */
charset_get_auto(void)441 gchar *charset_get_auto (void)
442 {
443     return g_strdup (auto_charset);
444 }
445 
charset_reset_auto(void)446 void charset_reset_auto (void)
447 {
448     auto_charset = NULL;
449 }
450 
451 
452 /* Convert "str" (in the charset specified in cfg->charset) to
453  * utf8. If cfg->charset is NULL, "str" is assumed to be in the
454  * current locale charset */
455 /* Must free the returned string yourself */
charset_to_utf8(const gchar * str)456 gchar *charset_to_utf8 (const gchar *str)
457 {
458     gchar *charset;  /* From prefs */
459     const gchar *locale_charset; /* Used if prefs doesn't have a charset */
460     gchar *result;
461 
462     if (str == NULL) return NULL;  /* sanity */
463     charset = charset_check_auto (str);
464     if (charset)
465     {
466 	g_free(auto_charset);
467 	auto_charset = g_strdup(charset);
468     }
469     else
470     {
471 	charset = prefs_get_string("charset");
472 	if (!charset || !strlen (charset))
473 	{    /* use standard locale charset */
474 	    g_free(charset);
475 	    g_get_charset (&locale_charset);
476 	    charset = g_strdup(locale_charset);
477 	}
478     }
479 
480     result = charset_to_charset (charset, "UTF-8", str);
481     g_free(charset);
482     return result;
483 }
484 
485 
486 /* Convert "str" from utf8 to the charset specified in
487  * cfg->charset. If cfg->charset is NULL, "str" is converted to the
488  * current locale charset */
489 /* Must free the returned string yourself */
charset_from_utf8(const gchar * str)490 gchar *charset_from_utf8 (const gchar *str)
491 {
492     gchar *charset;
493     const gchar *locale_charset;
494     gchar *result;
495 
496     if (str == NULL) return NULL;  /* sanity */
497     charset = prefs_get_string("charset");
498     if (!charset || !strlen (charset))
499     {
500        /* use standard locale charset */
501 	g_free(charset);
502 	g_get_charset (&locale_charset);
503 	charset = g_strdup(locale_charset);
504     }
505 
506     result = charset_to_charset ("UTF-8", charset, str);
507     g_free(charset);
508     return result;
509 }
510 
511 /* Convert "str" from utf8 to the charset specified in @s->charset. If
512  * this is NULL, try cfg->charset. If cfg->charset is also NULL, "str"
513  * is converted to the current locale charset */
514 /* Must free the returned string yourself */
charset_track_charset_from_utf8(Track * s,const gchar * str)515 gchar *charset_track_charset_from_utf8 (Track *s, const gchar *str)
516 {
517     gchar *charset;
518     const gchar *locale_charset;
519     gchar *result;
520     ExtraTrackData *etd;
521 
522     g_return_val_if_fail (s, NULL);
523     g_return_val_if_fail (s->userdata, NULL);
524 
525     if (str == NULL) return NULL;  /* sanity */
526 
527     etd = s->userdata;
528 
529     if (etd->charset && strlen (etd->charset))
530 	   charset = g_strdup(etd->charset);
531     else
532 	charset = prefs_get_string("charset");
533 
534     if (!charset || !strlen (charset))
535     {    /* use standard locale charset */
536 	g_free(charset);
537 	g_get_charset (&locale_charset);
538 	charset = g_strdup(locale_charset);
539     }
540 
541     result = charset_to_charset ("UTF-8", charset, str);
542     g_free(charset);
543     return result;
544 }
545 
546 /* Convert "str" from "from_charset" to "to_charset", trying to skip
547    illegal character as best as possible */
548 /* Must free the returned string yourself */
charset_to_charset(const gchar * from_charset,const gchar * to_charset,const gchar * str)549 gchar *charset_to_charset (const gchar *from_charset,
550 			   const gchar *to_charset,
551 			   const gchar *str)
552 {
553     gchar *ret;
554     gssize len;
555     gsize bytes_read;
556 
557     if (!str) return NULL;
558 
559     /* Handle automatic selection of Japanese charset */
560     if (from_charset && (strcmp (from_charset, GTKPOD_JAPAN_AUTOMATIC) == 0))
561 	from_charset = charset_check_k_code_with_default (str);
562     /* Automatic selection of Japanese charset when encoding to
563        Japanese is a bit of a problem... we simply fall back on EUC-JP
564        (defined in check_k_code_with_default ) if this situation
565        occurs. */
566     if (to_charset && (strcmp (to_charset, GTKPOD_JAPAN_AUTOMATIC) == 0))
567 	to_charset = charset_check_k_code_with_default (NULL);
568 
569     len = strlen (str);
570     /* do the conversion! */
571     ret = g_convert (str,                  /* string to convert */
572 		     len,                  /* length of string  */
573 		     to_charset,           /* to_codeset        */
574 		     from_charset,         /* from_codeset      */
575 		     &bytes_read,          /* *bytes_read       */
576 		     NULL,                 /* *bytes_written    */
577 		     NULL);                /* GError **error    */
578     if (ret == NULL)
579     { /* We probably had illegal characters. We try to convert as much
580        * as possible. "bytes_read" tells how many chars were converted
581        * successfully, and we try to fill up with spaces 0x20, which
582        * will work for most charsets (except those using 16 Bit or 32
583        * Bit representation (0x2020 or 0x20202020 != 0x0020 or
584        * 0x00000020) */
585 	gchar *strc = g_strdup (str);
586 	gsize br0 = bytes_read;
587 	gsize br;
588 	while (!ret && (bytes_read < len))
589 	{
590 	    strc[bytes_read] = 0x20;
591 	    br = bytes_read;
592 	    ret = g_convert (strc,         /* string to convert */
593 			     len,          /* length of string  */
594 			     to_charset,   /* to_codeset        */
595 			     from_charset, /* from_codeset      */
596 			     &bytes_read,  /* *bytes_read       */
597 			     NULL,         /* *bytes_written    */
598 			     NULL);        /* GError **error    */
599 	    /* max. nr. of converted Bytes */
600 	    if (bytes_read > br0)  br0 = bytes_read;
601 	    /* don't do an infinite loop */
602 	    if (bytes_read <= br)  bytes_read = br + 1;
603 	}
604 	if (ret == NULL)
605 	{ /* still no valid string */
606 	    if (br0 != 0)
607 	    { /* ok, at least something we can use! */
608 		ret = g_convert (strc,         /* string to convert */
609 				 br0,          /* length of string  */
610 				 to_charset,   /* to_codeset        */
611 				 from_charset, /* from_codeset      */
612 				 &bytes_read,  /* *bytes_read       */
613 				 NULL,         /* *bytes_written    */
614 				 NULL);        /* GError **error    */
615 	    }
616 	    if (ret == NULL)
617 	    { /* Well... what do you think we should return? */
618 		/* ret = g_strdup (_("gtkpod: Invalid Conversion")); */
619 		ret = g_strdup ("");
620 	    }
621 	}
622 	g_free (strc);
623     }
624     return ret;
625 }
626