Pth tweaks and improved estream.c
[gnupg.git] / jnlib / utf8conv.c
1 /* utf8conf.c -  UTF8 character set conversion
2  * Copyright (C) 1994, 1998, 1999, 2000, 2001,
3  *               2003, 2006  Free Software Foundation, Inc.
4  *
5  * This file is part of JNLIB.
6  *
7  * JNLIB is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as
9  * published by the Free Software Foundation; either version 2.1 of
10  * the License, or (at your option) any later version.
11  *
12  * JNLIB is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20  * 02110-1301, USA.
21  */
22
23 #include <config.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #include <ctype.h>
28 #ifdef HAVE_LANGINFO_CODESET
29 #include <langinfo.h>
30 #endif
31 #include <errno.h>
32 #include <iconv.h>
33
34 #include "libjnlib-config.h"
35 #include "stringhelp.h"
36 #include "utf8conv.h"
37
38 #ifndef MB_LEN_MAX
39 #define MB_LEN_MAX 16
40 #endif
41
42 static const char *active_charset_name = "iso-8859-1";
43 static unsigned short *active_charset;
44 static int no_translation;     /* Set to true if we let simply pass through. */
45 static int use_iconv;          /* iconv comversion fucntions required. */
46
47
48
49 /* Error handler for iconv failures. This is needed to not clutter the
50    output with repeated diagnostics about a missing conversion. */
51 static void
52 handle_iconv_error (const char *to, const char *from, int use_fallback)
53 {
54   if (errno == EINVAL)
55     {
56       static int shown1, shown2;
57       int x;
58
59       if (to && !strcmp (to, "utf-8"))
60         {
61           x = shown1;
62           shown1 = 1;
63         }
64       else
65         {
66           x = shown2;
67           shown2 = 1;
68         }
69
70       if (!x)
71         log_info (_("conversion from `%s' to `%s' not available\n"),
72                   from, to);
73     }
74   else
75     {
76       static int shown;
77
78       if (!shown)
79         log_info (_("iconv_open failed: %s\n"), strerror (errno));
80       shown = 1;
81     }
82
83   if (use_fallback)
84     {
85       /* To avoid further error messages we fallback to Latin-1 for the
86          native encoding.  This is justified as one can expect that on a
87          utf-8 enabled system nl_langinfo() will work and thus we won't
88          never get to here.  Thus Latin-1 seems to be a reasonable
89          default.  */
90       active_charset_name = "iso-8859-1";
91       no_translation = 0;
92       active_charset = NULL;
93       use_iconv = 0;
94     }
95 }
96
97
98 int
99 set_native_charset (const char *newset)
100 {
101   const char *full_newset;
102
103   if (!newset) 
104     {
105 #ifdef HABE_W32_SYSTEM
106       static char codepage[30];
107       unsigned int cpno;
108       const char *aliases;
109       
110       /* We are a console program thus we need to use the
111          GetConsoleOutputCP function and not the the GetACP which
112          would give the codepage for a GUI program.  Note this is not
113          a bulletproof detection because GetConsoleCP might return a
114          different one for console input.  Not sure how to cope with
115          that.  If the console Code page is not known we fall back to
116          the system code page.  */
117       cpno = GetConsoleOutputCP ();
118       if (!cpno)
119         cpno = GetACP ();
120       sprintf (codepage, "CP%u", cpno );
121       /* Resolve alias.  We use a long string string and not the usual
122          array to optimize if the code is taken to a DSO.  Taken from
123          libiconv 1.9.2. */
124       newset = codepage;
125       for (aliases = ("CP936"   "\0" "GBK" "\0"
126                       "CP1361"  "\0" "JOHAB" "\0"
127                       "CP20127" "\0" "ASCII" "\0"
128                       "CP20866" "\0" "KOI8-R" "\0"
129                       "CP21866" "\0" "KOI8-RU" "\0"
130                       "CP28591" "\0" "ISO-8859-1" "\0"
131                       "CP28592" "\0" "ISO-8859-2" "\0"
132                       "CP28593" "\0" "ISO-8859-3" "\0"
133                       "CP28594" "\0" "ISO-8859-4" "\0"
134                       "CP28595" "\0" "ISO-8859-5" "\0"
135                       "CP28596" "\0" "ISO-8859-6" "\0"
136                       "CP28597" "\0" "ISO-8859-7" "\0"
137                       "CP28598" "\0" "ISO-8859-8" "\0"
138                       "CP28599" "\0" "ISO-8859-9" "\0"
139                       "CP28605" "\0" "ISO-8859-15" "\0"
140                       "CP65001" "\0" "UTF-8" "\0");
141            *aliases;
142            aliases += strlen (aliases) + 1, aliases += strlen (aliases) + 1)
143         {
144           if (!strcmp (codepage, aliases) ||(*aliases == '*' && !aliases[1]))
145             {
146               newset = aliases + strlen (aliases) + 1;
147               break;
148             }
149         }
150
151 #else /*!HAVE_W32_SYSTEM*/
152       
153 #ifdef HAVE_LANGINFO_CODESET
154       newset = nl_langinfo (CODESET);
155 #else /*!HAVE_LANGINFO_CODESET*/
156       /* Try to get the used charset from environment variables.  */
157       static char codepage[30];
158       const char *lc, *dot, *mod;
159
160       strcpy (codepage, "iso-8859-1");
161       lc = getenv ("LC_ALL");
162       if (!lc || !*lc)
163         {
164           lc = getenv ("LC_CTYPE");
165           if (!lc || !*lc)
166             lc = getenv ("LANG");
167         }
168       if (lc && *lc)
169         {
170           dot = strchr (lc, '.');
171           if (dot)
172             {
173               mod = strchr (++dot, '@');
174               if (!mod)
175                 mod = dot + strlen (dot);
176               if (mod - dot < sizeof codepage && dot != mod) 
177                 {
178                   memcpy (codepage, dot, mod - dot);
179                   codepage [mod - dot] = 0;
180                 }
181             }
182         }
183       newset = codepage;
184 #endif /*!HAVE_LANGINFO_CODESET*/
185 #endif /*!HAVE_W32_SYSTEM*/
186     }
187
188   full_newset = newset;
189   if (strlen (newset) > 3 && !ascii_memcasecmp (newset, "iso", 3))
190     {
191       newset += 3;
192       if (*newset == '-' || *newset == '_')
193         newset++;
194     }
195
196   /* Note that we silently assume that plain ASCII is actually meant
197      as Latin-1.  This makes sense because many Unix system don't have
198      their locale set up properly and thus would get annoying error
199      messages and we have to handle all the "bug" reports. Latin-1 has
200      always been the character set used for 8 bit characters on Unix
201      systems. */
202   if ( !*newset
203        || !ascii_strcasecmp (newset, "8859-1" )
204        || !ascii_strcasecmp (newset, "646" )
205        || !ascii_strcasecmp (newset, "ASCII" )
206        || !ascii_strcasecmp (newset, "ANSI_X3.4-1968" )
207        )
208     {
209       active_charset_name = "iso-8859-1";
210       no_translation = 0;
211       active_charset = NULL;
212       use_iconv = 0;
213     }
214   else if ( !ascii_strcasecmp (newset, "utf8" )
215             || !ascii_strcasecmp(newset, "utf-8") )
216     {
217       active_charset_name = "utf-8";
218       no_translation = 1;
219       active_charset = NULL;
220       use_iconv = 0;
221     }
222   else
223     {
224       iconv_t cd;
225       
226 #ifdef HAVE_W32_SYSTEM
227       if (load_libiconv ())
228         return -1;
229 #endif /*HAVE_W32_SYSTEM*/      
230
231       cd = iconv_open (full_newset, "utf-8");
232       if (cd == (iconv_t)-1) 
233         {
234           handle_iconv_error (full_newset, "utf-8", 0);
235           return -1;
236         }
237       iconv_close (cd);
238       cd = iconv_open ("utf-8", full_newset);
239       if (cd == (iconv_t)-1) 
240         {
241           handle_iconv_error ("utf-8", full_newset, 0);
242           return -1;
243         }
244       iconv_close (cd);
245       active_charset_name = full_newset;
246       no_translation = 0;
247       active_charset = NULL; 
248       use_iconv = 1;
249     }
250   return 0;
251 }
252
253 const char *
254 get_native_charset ()
255 {
256   return active_charset_name;
257 }
258
259
260 /* Convert string, which is in native encoding to UTF8 and return a
261    new allocated UTF-8 string.  */
262 char *
263 native_to_utf8 (const char *orig_string)
264 {
265   const unsigned char *string = (const unsigned char *)orig_string;
266   const unsigned char *s;
267   char *buffer;
268   unsigned char *p;
269   size_t length = 0;
270
271   if (no_translation)
272     {
273       /* Already utf-8 encoded. */
274       buffer = jnlib_xstrdup (orig_string);
275     }
276   else if (!active_charset && !use_iconv)
277     {
278       /* For Latin-1 we can avoid the iconv overhead. */
279       for (s = string; *s; s++)
280         {
281           length++;
282           if (*s & 0x80)
283             length++;
284         }
285       buffer = jnlib_xmalloc (length + 1);
286       for (p = (unsigned char *)buffer, s = string; *s; s++)
287         {
288           if ( (*s & 0x80 ))
289             {
290               *p++ = 0xc0 | ((*s >> 6) & 3);
291               *p++ = 0x80 | (*s & 0x3f);
292             }
293           else
294             *p++ = *s;
295         }
296       *p = 0;
297     }
298   else
299     { 
300       /* Need to use iconv.  */
301       iconv_t cd;
302       const char *inptr;
303       char *outptr;
304       size_t inbytes, outbytes;
305      
306       cd = iconv_open ("utf-8", active_charset_name);
307       if (cd == (iconv_t)-1)
308         {
309           handle_iconv_error ("utf-8", active_charset_name, 1);
310           return native_to_utf8 (string);
311         }
312
313       for (s=string; *s; s++ ) 
314         {
315           length++;
316           if ((*s & 0x80))
317             length += 5; /* We may need up to 6 bytes for the utf8 output. */
318         }
319       buffer = jnlib_xmalloc (length + 1);
320       
321       inptr = string;
322       inbytes = strlen (string);
323       outptr = buffer;
324       outbytes = length;
325       if ( iconv (cd, (ICONV_CONST char **)&inptr, &inbytes,
326                   &outptr, &outbytes) == (size_t)-1)
327         {
328           static int shown;
329
330           if (!shown)
331             log_info (_("conversion from `%s' to `%s' failed: %s\n"),
332                       active_charset_name, "utf-8", strerror (errno));
333           shown = 1;
334           /* We don't do any conversion at all but use the strings as is. */
335           strcpy (buffer, string);
336         }
337       else /* Success.  */
338         {
339           *outptr = 0;
340           /* We could realloc the buffer now but I doubt that it makes
341              much sense given that it will get freed anyway soon
342              after.  */
343         }
344       iconv_close (cd);
345     }
346   return buffer;
347 }
348
349
350
351 static char *
352 do_utf8_to_native (const char *string, size_t length, int delim,
353                    int with_iconv)
354 {
355   int nleft;
356   int i;
357   unsigned char encbuf[8];
358   int encidx;
359   const unsigned char *s;
360   size_t n;
361   char *buffer = NULL;
362   char *p = NULL;
363   unsigned long val = 0;
364   size_t slen;
365   int resync = 0;
366
367   /* First pass (p==NULL): count the extended utf-8 characters.  */
368   /* Second pass (p!=NULL): create string.  */
369   for (;;)
370     {
371       for (slen = length, nleft = encidx = 0, n = 0,
372              s = (const unsigned char *)string;
373            slen;
374            s++, slen--)
375         {
376           if (resync)
377             {
378               if (!(*s < 128 || (*s >= 0xc0 && *s <= 0xfd)))
379                 {
380                   /* Still invalid. */
381                   if (p)
382                     {
383                       sprintf (p, "\\x%02x", *s);
384                       p += 4;
385                     }
386                   n += 4;
387                   continue;
388                 }
389               resync = 0;
390             }
391           if (!nleft)
392             {
393               if (!(*s & 0x80))
394                 {       
395                   /* Plain ascii. */
396                   if ( delim != -1
397                        && (*s < 0x20 || *s == 0x7f || *s == delim 
398                            || (delim && *s == '\\')))
399                     {
400                       n++;
401                       if (p)
402                         *p++ = '\\';
403                       switch (*s)
404                         {
405                         case '\n': n++; if ( p ) *p++ = 'n'; break;
406                         case '\r': n++; if ( p ) *p++ = 'r'; break;
407                         case '\f': n++; if ( p ) *p++ = 'f'; break;
408                         case '\v': n++; if ( p ) *p++ = 'v'; break;
409                         case '\b': n++; if ( p ) *p++ = 'b'; break;
410                         case    0: n++; if ( p ) *p++ = '0'; break;
411                         default:
412                           n += 3;
413                           if (p)
414                             {
415                               sprintf (p, "x%02x", *s);
416                               p += 3;
417                             }
418                           break;
419                         }
420                     }
421                   else
422                     {
423                       if (p)
424                         *p++ = *s;
425                       n++;
426                     }
427                 }
428               else if ((*s & 0xe0) == 0xc0) /* 110x xxxx */
429                 {
430                   val = *s & 0x1f;
431                   nleft = 1;
432                   encidx = 0;
433                   encbuf[encidx++] = *s;
434                 }
435               else if ((*s & 0xf0) == 0xe0) /* 1110 xxxx */
436                 {       
437                   val = *s & 0x0f;
438                   nleft = 2;
439                   encidx = 0;
440                   encbuf[encidx++] = *s;
441                 }
442               else if ((*s & 0xf8) == 0xf0) /* 1111 0xxx */
443                 {       
444                   val = *s & 0x07;
445                   nleft = 3;
446                   encidx = 0;
447                   encbuf[encidx++] = *s;
448                 }
449               else if ((*s & 0xfc) == 0xf8) /* 1111 10xx */
450                 {       
451                   val = *s & 0x03;
452                   nleft = 4;
453                   encidx = 0;
454                   encbuf[encidx++] = *s;
455                 }
456               else if ((*s & 0xfe) == 0xfc) /* 1111 110x */
457                 {               
458                   val = *s & 0x01;
459                   nleft = 5;
460                   encidx = 0;
461                   encbuf[encidx++] = *s;
462                 }
463               else /* Invalid encoding: print as \xNN. */
464                 {               
465                   if (p)
466                     {
467                       sprintf (p, "\\x%02x", *s);
468                       p += 4;
469                     }
470                   n += 4;
471                   resync = 1;
472                 }
473             }
474           else if (*s < 0x80 || *s >= 0xc0) /* Invalid utf-8 */
475             {
476               if (p)
477                 {
478                   for (i = 0; i < encidx; i++)
479                     {
480                       sprintf (p, "\\x%02x", encbuf[i]);
481                       p += 4;
482                     }
483                   sprintf (p, "\\x%02x", *s);
484                   p += 4;
485                 }
486               n += 4 + 4 * encidx;
487               nleft = 0;
488               encidx = 0;
489               resync = 1;
490             }
491           else
492             {
493               encbuf[encidx++] = *s;
494               val <<= 6;
495               val |= *s & 0x3f;
496               if (!--nleft)  /* Ready. */
497                 { 
498                   if (no_translation)
499                     {
500                       if (p)
501                         {
502                           for (i = 0; i < encidx; i++)
503                             *p++ = encbuf[i];
504                         }
505                       n += encidx;
506                       encidx = 0;
507                     }
508                   else if (with_iconv)
509                     {
510                       /* Our strategy for using iconv is a bit strange
511                          but it better keeps compatibility with
512                          previous versions in regard to how invalid
513                          encodings are displayed.  What we do is to
514                          keep the utf-8 as is and have the real
515                          translation step then at the end.  Yes, I
516                          know that this is ugly.  However we are short
517                          of the 1.4 release and for this branch we
518                          should not mess too much around with iconv
519                          things.  One reason for this is that we don't
520                          know enough about non-GNU iconv
521                          implementation and want to minimize the risk
522                          of breaking the code on too many platforms.  */
523                         if ( p )
524                           {
525                             for (i=0; i < encidx; i++ )
526                               *p++ = encbuf[i];
527                           }
528                         n += encidx;
529                         encidx = 0;
530                     }
531                   else  /* Latin-1 case. */
532                     {
533                       if (val >= 0x80 && val < 256)
534                         {
535                           /* We can simply print this character */
536                           n++;  
537                           if (p)
538                             *p++ = val;
539                         }
540                       else
541                         {       
542                           /* We do not have a translation: print utf8. */
543                           if (p)
544                             {
545                               for (i = 0; i < encidx; i++)
546                                 {
547                                   sprintf (p, "\\x%02x", encbuf[i]);
548                                   p += 4;
549                                 }
550                             }
551                           n += encidx * 4;
552                           encidx = 0;
553                         }
554                     }
555                 }
556
557             }
558         }
559       if (!buffer)
560         {
561           /* Allocate the buffer after the first pass. */
562           buffer = p = jnlib_xmalloc (n + 1);
563         }
564       else if (with_iconv)
565         {
566           /* Note: See above for comments.  */
567           iconv_t cd;
568           const char *inptr;
569           char *outbuf, *outptr;
570           size_t inbytes, outbytes;
571           
572           *p = 0;  /* Terminate the buffer. */
573
574           cd = iconv_open (active_charset_name, "utf-8");
575           if (cd == (iconv_t)-1)
576             {
577               handle_iconv_error (active_charset_name, "utf-8", 1);
578               jnlib_free (buffer);
579               return utf8_to_native (string, length, delim);
580             }
581
582           /* Allocate a new buffer large enough to hold all possible
583              encodings. */
584           n = p - buffer + 1;
585           inbytes = n - 1;;
586           inptr = buffer;
587           outbytes = n * MB_LEN_MAX;
588           if (outbytes / MB_LEN_MAX != n) 
589             BUG (); /* Actually an overflow. */
590           outbuf = outptr = jnlib_xmalloc (outbytes);
591           if ( iconv (cd, (ICONV_CONST char **)&inptr, &inbytes,
592                       &outptr, &outbytes) == (size_t)-1) 
593             {
594               static int shown;
595               
596               if (!shown)
597                 log_info (_("conversion from `%s' to `%s' failed: %s\n"),
598                           "utf-8", active_charset_name, strerror (errno));
599               shown = 1;
600               /* Didn't worked out.  Try again but without iconv.  */
601               jnlib_free (buffer);
602               buffer = NULL;
603               jnlib_free (outbuf);
604               outbuf = do_utf8_to_native (string, length, delim, 0);
605             }
606             else /* Success.  */
607               { 
608                 *outptr = 0; /* Make sure it is a string. */
609                 /* We could realloc the buffer now but I doubt that it
610                    makes much sense given that it will get freed
611                    anyway soon after.  */
612                 jnlib_free (buffer);
613               }
614           iconv_close (cd);
615           return outbuf;
616         }
617       else /* Not using iconv. */
618         {
619           *p = 0; /* Make sure it is a string. */
620           return buffer;
621         }
622     }
623 }
624
625 /* Convert string, which is in UTF-8 to native encoding.  Replace
626    illegal encodings by some "\xnn" and quote all control
627    characters. A character with value DELIM will always be quoted, it
628    must be a vanilla ASCII character.  A DELIM value of -1 is special:
629    it disables all quoting of control characters. */
630 char *
631 utf8_to_native (const char *string, size_t length, int delim)
632 {
633   return do_utf8_to_native (string, length, delim, use_iconv);
634 }