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