*: small code shrinks and compile fix for unicode
[platform/upstream/busybox.git] / libbb / unicode.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Unicode support routines.
4  *
5  * Copyright (C) 2009 Denys Vlasenko
6  *
7  * Licensed under GPL version 2, see file LICENSE in this tarball for details.
8  */
9 #include "libbb.h"
10 #include "unicode.h"
11
12 /* If it's not a constant... */
13 #ifndef unicode_status
14 uint8_t unicode_status;
15 #endif
16
17 size_t FAST_FUNC bb_mbstrlen(const char *string)
18 {
19         size_t width = mbstowcs(NULL, string, INT_MAX);
20         if (width == (size_t)-1L)
21                 return strlen(string);
22         return width;
23 }
24
25 #if ENABLE_LOCALE_SUPPORT
26
27 /* Unicode support using libc */
28
29 void FAST_FUNC init_unicode(void)
30 {
31         /* In unicode, this is a one character string */
32         static const char unicode_0x394[] = { 0xce, 0x94, 0 };
33
34         if (unicode_status != UNICODE_UNKNOWN)
35                 return;
36
37         unicode_status = bb_mbstrlen(unicode_0x394) == 1 ? UNICODE_ON : UNICODE_OFF;
38 }
39
40 #else
41
42 /* Crude "locale support" which knows only C and Unicode locales */
43
44 # if ENABLE_FEATURE_CHECK_UNICODE_IN_ENV
45 void FAST_FUNC init_unicode(void)
46 {
47         char *lang;
48
49         if (unicode_status != UNICODE_UNKNOWN)
50                 return;
51
52         unicode_status = UNICODE_OFF;
53         lang = getenv("LANG");
54         if (!lang || !(strstr(lang, ".utf") || strstr(lang, ".UTF")))
55                 return;
56         unicode_status = UNICODE_ON;
57 }
58 # endif
59
60 static size_t wcrtomb_internal(char *s, wchar_t wc)
61 {
62         int n, i;
63         uint32_t v = wc;
64
65         if (v <= 0x7f) {
66                 *s = v;
67                 return 1;
68         }
69
70         /* RFC 3629 says that Unicode ends at 10FFFF,
71          * but we cover entire 32 bits */
72
73         /* 4000000-FFFFFFFF -> 111111tt 10tttttt 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx */
74         /* 200000-3FFFFFF -> 111110tt 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx */
75         /* 10000-1FFFFF -> 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx */
76         /* 800-FFFF -> 1110yyyy 10yyyyxx 10xxxxxx */
77         /* 80-7FF -> 110yyyxx 10xxxxxx */
78
79         /* How many bytes do we need? */
80         n = 2;
81         /* (0x80000000+ would result in n = 7, limiting n to 6) */
82         while (v >= 0x800 && n < 6) {
83                 v >>= 5;
84                 n++;
85         }
86         /* Fill bytes n-1..1 */
87         i = n;
88         while (--i) {
89                 s[i] = (wc & 0x3f) | 0x80;
90                 wc >>= 6;
91         }
92         /* Fill byte 0 */
93         s[0] = wc | (uint8_t)(0x3f00 >> n);
94         return n;
95 }
96
97 size_t FAST_FUNC wcrtomb(char *s, wchar_t wc, mbstate_t *ps UNUSED_PARAM)
98 {
99         if (unicode_status != UNICODE_ON) {
100                 *s = wc;
101                 return 1;
102         }
103
104         return wcrtomb_internal(s, wc);
105 }
106
107 size_t FAST_FUNC wcstombs(char *dest, const wchar_t *src, size_t n)
108 {
109         size_t org_n = n;
110
111         if (unicode_status != UNICODE_ON) {
112                 while (n) {
113                         wchar_t c = *src++;
114                         *dest++ = c;
115                         if (c == 0)
116                                 break;
117                         n--;
118                 }
119                 return org_n - n;
120         }
121
122         while (n >= MB_CUR_MAX) {
123                 wchar_t wc = *src++;
124                 size_t len = wcrtomb_internal(dest, wc);
125
126                 if (wc == L'\0')
127                         return org_n - n;
128                 dest += len;
129                 n -= len;
130         }
131         while (n) {
132                 char tbuf[MB_CUR_MAX];
133                 wchar_t wc = *src++;
134                 size_t len = wcrtomb_internal(tbuf, wc);
135
136                 if (len > n)
137                         len = n;
138                 memcpy(dest, tbuf, len);
139                 if (wc == L'\0')
140                         return org_n - n;
141                 dest += len;
142                 n -= len;
143         }
144         return org_n - n;
145 }
146
147 size_t FAST_FUNC mbstowcs(wchar_t *dest, const char *src, size_t n)
148 {
149         size_t org_n = n;
150
151         if (unicode_status != UNICODE_ON) {
152                 while (n) {
153                         unsigned char c = *src++;
154
155                         if (dest)
156                                 *dest++ = c;
157                         if (c == 0)
158                                 break;
159                         n--;
160                 }
161                 return org_n - n;
162         }
163
164         while (n) {
165                 int bytes;
166                 unsigned c = (unsigned char) *src++;
167
168                 if (c <= 0x7f) {
169                         if (dest)
170                                 *dest++ = c;
171                         if (c == '\0')
172                                 break;
173                         n--;
174                         continue;
175                 }
176
177                 /* 80-7FF -> 110yyyxx 10xxxxxx */
178                 /* 800-FFFF -> 1110yyyy 10yyyyxx 10xxxxxx */
179                 /* 10000-1FFFFF -> 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx */
180                 /* 200000-3FFFFFF -> 111110tt 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx */
181                 /* 4000000-FFFFFFFF -> 111111tt 10tttttt 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx */
182                 bytes = 0;
183                 do {
184                         c <<= 1;
185                         bytes++;
186                 } while ((c & 0x80) && bytes < 6);
187                 if (bytes == 1)
188                         return (size_t) -1L;
189                 c = (uint8_t)(c) >> bytes;
190
191                 while (--bytes) {
192                         unsigned ch = (unsigned char) *src++;
193                         if ((ch & 0xc0) != 0x80) {
194                                 return (size_t) -1L;
195                         }
196                         c = (c << 6) + (ch & 0x3f);
197                 }
198
199                 /* TODO */
200                 /* Need to check that c isn't produced by overlong encoding */
201                 /* Example: 11000000 10000000 converts to NUL */
202                 /* 11110000 10000000 10000100 10000000 converts to 0x100 */
203                 /* correct encoding: 11000100 10000000 */
204                 if (c <= 0x7f) { /* crude check */
205                         return (size_t) -1L;
206                         //or maybe: c = 0xfffd; /* replacement character */
207                 }
208
209                 if (dest)
210                         *dest++ = c;
211                 n--;
212         }
213
214         return org_n - n;
215 }
216
217 int FAST_FUNC iswspace(wint_t wc)
218 {
219         return (unsigned)wc <= 0x7f && isspace(wc);
220 }
221
222 int FAST_FUNC iswalnum(wint_t wc)
223 {
224         return (unsigned)wc <= 0x7f && isalnum(wc);
225 }
226
227 int FAST_FUNC iswpunct(wint_t wc)
228 {
229         return (unsigned)wc <= 0x7f && ispunct(wc);
230 }
231
232 #endif