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