No specific user configuration
[platform/upstream/bash.git] / lib / sh / unicode.c
1 /* unicode.c - functions to convert unicode characters */
2
3 /* Copyright (C) 2010-2012 Free Software Foundation, Inc.
4
5    This file is part of GNU Bash, the Bourne Again SHell.
6
7    Bash is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation, either version 3 of the License, or
10    (at your option) any later version.
11
12    Bash is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with Bash.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <config.h>
22
23 #if defined (HANDLE_MULTIBYTE)
24
25 #include <stdc.h>
26 #include <wchar.h>
27 #include <bashansi.h>
28 #ifdef HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31 #include <stdio.h>
32 #include <limits.h>
33
34 #if HAVE_ICONV
35 #  include <iconv.h>
36 #endif
37
38 #include <xmalloc.h>
39
40 #ifndef USHORT_MAX
41 #  ifdef USHRT_MAX
42 #    define USHORT_MAX USHRT_MAX
43 #  else
44 #    define USHORT_MAX ((unsigned short) ~(unsigned short)0)
45 #  endif
46 #endif
47
48 #if !defined (STREQ)
49 #  define STREQ(a, b) ((a)[0] == (b)[0] && strcmp ((a), (b)) == 0)
50 #endif /* !STREQ */
51
52 #if defined (HAVE_LOCALE_CHARSET)
53 extern const char *locale_charset __P((void));
54 #else
55 extern char *get_locale_var __P((char *));
56 #endif
57
58 static int u32init = 0;
59 static int utf8locale = 0;
60 #if defined (HAVE_ICONV)
61 static iconv_t localconv;
62 #endif
63
64 #ifndef HAVE_LOCALE_CHARSET
65 static char charsetbuf[40];
66
67 static char *
68 stub_charset ()
69 {
70   char *locale, *s, *t;
71
72   locale = get_locale_var ("LC_CTYPE");
73   if (locale == 0 || *locale == 0)
74     {
75       strcpy (charsetbuf, "ASCII");
76       return charsetbuf;
77     }
78   s = strrchr (locale, '.');
79   if (s)
80     {
81       strcpy (charsetbuf, s+1);
82       t = strchr (charsetbuf, '@');
83       if (t)
84         *t = 0;
85       return charsetbuf;
86     }
87   strcpy (charsetbuf, locale);
88   return charsetbuf;
89 }
90 #endif
91
92 void
93 u32reset ()
94 {
95 #if defined (HAVE_ICONV)
96   if (u32init && localconv != (iconv_t)-1)
97     {
98       iconv_close (localconv);
99       localconv = (iconv_t)-1;
100     }
101 #endif
102   u32init = 0;
103   utf8locale = 0;
104 }
105
106 /* u32toascii ? */
107 int
108 u32tochar (x, s)
109      unsigned long x;
110      char *s;
111 {
112   int l;
113
114   l = (x <= UCHAR_MAX) ? 1 : ((x <= USHORT_MAX) ? 2 : 4);
115
116   if (x <= UCHAR_MAX)
117     s[0] = x & 0xFF;
118   else if (x <= USHORT_MAX)     /* assume unsigned short = 16 bits */
119     {
120       s[0] = (x >> 8) & 0xFF;
121       s[1] = x & 0xFF;
122     }
123   else
124     {
125       s[0] = (x >> 24) & 0xFF;
126       s[1] = (x >> 16) & 0xFF;
127       s[2] = (x >> 8) & 0xFF;
128       s[3] = x & 0xFF;
129     }
130   s[l] = '\0';
131   return l;  
132 }
133
134 int
135 u32tocesc (wc, s)
136      u_bits32_t wc;
137      char *s;
138 {
139   int l;
140
141   if (wc < 0x10000)
142     l = sprintf (s, "\\u%04X", wc);
143   else
144     l = sprintf (s, "\\u%08X", wc);
145   return l;
146 }
147
148 /* Convert unsigned 32-bit int to utf-8 character string */
149 int
150 u32toutf8 (wc, s)
151      u_bits32_t wc;
152      char *s;
153 {
154   int l;
155
156   if (wc < 0x0080)
157     {
158       s[0] = (char)wc;
159       l = 1;
160     }
161   else if (wc < 0x0800)
162     {
163       s[0] = (wc >> 6) | 0xc0;
164       s[1] = (wc & 0x3f) | 0x80;
165       l = 2;
166     }
167   else if (wc < 0x10000)
168     {
169       /* Technically, we could return 0 here if 0xd800 <= wc <= 0x0dfff */
170       s[0] = (wc >> 12) | 0xe0;
171       s[1] = ((wc >> 6) & 0x3f) | 0x80;
172       s[2] = (wc & 0x3f) | 0x80;
173       l = 3;
174     }
175   else if (wc < 0x200000)
176     {
177       s[0] = (wc >> 18) | 0xf0;
178       s[1] = ((wc >> 12) & 0x3f) | 0x80;
179       s[2] = ((wc >>  6) & 0x3f) | 0x80;
180       s[3] = (wc & 0x3f) | 0x80;
181       l = 4;
182     }
183   /* Strictly speaking, UTF-8 doesn't have characters longer than 4 bytes */
184   else if (wc < 0x04000000)
185     {
186       s[0] = (wc >> 24) | 0xf8;
187       s[1] = ((wc >> 18) & 0x3f) | 0x80;
188       s[2] = ((wc >> 12) & 0x3f) | 0x80;
189       s[3] = ((wc >>  6) & 0x3f) | 0x80;
190       s[4] = (wc & 0x3f) | 0x80;
191       l = 5;
192     }
193   else if (wc < 0x080000000)
194     {
195       s[0] = (wc >> 30) | 0xf8;
196       s[1] = ((wc >> 24) & 0x3f) | 0x80;
197       s[2] = ((wc >> 18) & 0x3f) | 0x80;
198       s[3] = ((wc >> 12) & 0x3f) | 0x80;
199       s[4] = ((wc >>  6) & 0x3f) | 0x80;
200       s[5] = (wc & 0x3f) | 0x80;
201       l = 6;
202     }
203   else
204     l = 0;
205
206   s[l] = '\0';
207   return l;
208 }
209
210 /* Convert a 32-bit unsigned int (unicode) to a UTF-16 string.  Rarely used,
211    only if sizeof(wchar_t) == 2. */
212 int
213 u32toutf16 (c, s)
214      u_bits32_t c;
215      unsigned short *s;
216 {
217   int l;
218
219   l = 0;
220   if (c < 0x0d800)
221     {
222       s[0] = (unsigned short) (c & 0xFFFF);
223       l = 1;
224     }
225   else if (c >= 0x0e000 && c <= 0x010ffff)
226     {
227       c -= 0x010000;
228       s[0] = (unsigned short)((c >> 10) + 0xd800);
229       s[1] = (unsigned short)((c & 0x3ff) + 0xdc00);
230       l = 2;
231     }
232   s[l] = 0;
233   return l;
234 }
235
236 /* convert a single unicode-32 character into a multibyte string and put the
237    result in S, which must be large enough (at least MB_LEN_MAX bytes) */
238 int
239 u32cconv (c, s)
240      unsigned long c;
241      char *s;
242 {
243   wchar_t wc;
244   wchar_t ws[3];
245   int n;
246 #if HAVE_ICONV
247   const char *charset;
248   char obuf[25], *optr;
249   size_t obytesleft;
250   const char *iptr;
251   size_t sn;
252 #endif
253
254 #if __STDC_ISO_10646__
255   wc = c;
256   if (sizeof (wchar_t) == 4 && c <= 0x7fffffff)
257     n = wctomb (s, wc);
258   else if (sizeof (wchar_t) == 2 && c <= 0x10ffff && u32toutf16 (c, ws))
259     n = wcstombs (s, ws, MB_LEN_MAX);
260   else
261     n = -1;
262   if (n != -1)
263     return n;
264 #endif
265
266 #if HAVE_NL_LANGINFO
267   codeset = nl_langinfo (CODESET);
268   if (STREQ (codeset, "UTF-8"))
269     {
270       n = u32toutf8 (c, s);
271       return n;
272     }
273 #endif
274
275 #if HAVE_ICONV
276   /* this is mostly from coreutils-8.5/lib/unicodeio.c */
277   if (u32init == 0)
278     {
279 #  if HAVE_LOCALE_CHARSET
280       charset = locale_charset ();      /* XXX - fix later */
281 #  else
282       charset = stub_charset ();
283 #  endif
284       if (STREQ (charset, "UTF-8"))
285         utf8locale = 1;
286       else
287         {
288           localconv = iconv_open (charset, "UTF-8");
289           if (localconv == (iconv_t)-1)
290             /* We assume ASCII when presented with an unknown encoding. */
291             localconv = iconv_open ("ASCII", "UTF-8");
292         }
293       u32init = 1;
294     }
295
296   /* If we have a UTF-8 locale, convert to UTF-8 and return converted value. */
297   n = u32toutf8 (c, s);
298   if (utf8locale)
299     return n;
300
301   /* If the conversion is not supported, even the ASCII requested above, we
302      bail now.  Currently we return the UTF-8 conversion.  We could return
303      u32tocesc(). */
304   if (localconv == (iconv_t)-1)
305     return n;
306     
307   optr = obuf;
308   obytesleft = sizeof (obuf);
309   iptr = s;
310   sn = n;
311
312   iconv (localconv, NULL, NULL, NULL, NULL);
313
314   if (iconv (localconv, (ICONV_CONST char **)&iptr, &sn, &optr, &obytesleft) == (size_t)-1)
315     {
316 #if 1
317       /* You get ISO C99 escape sequences if iconv fails */      
318       n = u32tocesc (c, s);
319 #else
320       /* You get UTF-8 if iconv fails */
321 #endif
322       return n;
323     }
324
325   *optr = '\0';
326
327   /* number of chars to be copied is optr - obuf if we want to do bounds
328      checking */
329   strcpy (s, obuf);
330   return (optr - obuf);
331 #endif  /* HAVE_ICONV */
332
333   n = u32tocesc (c, s); /* fallback is ISO C99 escape sequences */
334   return n;
335 }
336 #else
337 void
338 u32reset ()
339 {
340 }
341 #endif /* HANDLE_MULTIBYTE */