source: MondoRescue/branches/3.3/mindi-busybox/libbb/unicode.c@ 3906

Last change on this file since 3906 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

  • Property svn:eol-style set to native
File size: 31.5 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Unicode support routines.
4 *
5 * Copyright (C) 2009 Denys Vlasenko
6 *
7 * Licensed under GPLv2, see file LICENSE in this source tree.
8 */
9#include "libbb.h"
10#include "unicode.h"
11
12/* If it's not #defined as a constant in unicode.h... */
13#ifndef unicode_status
14uint8_t unicode_status;
15#endif
16
17/* This file is compiled only if UNICODE_SUPPORT is on.
18 * We check other options and decide whether to use libc support
19 * via locale, or use our own logic:
20 */
21
22#if ENABLE_UNICODE_USING_LOCALE
23
24/* Unicode support using libc locale support. */
25
26void FAST_FUNC reinit_unicode(const char *LANG)
27{
28 static const char unicode_0x394[] = { 0xce, 0x94, 0 };
29 size_t width;
30
31 /* We pass "" instead of "C" because some libc's have
32 * non-ASCII default locale for setlocale("") call
33 * (this allows users of such libc to have Unicoded
34 * system without having to mess with env).
35 *
36 * We set LC_CTYPE because (a) we may be called with $LC_CTYPE
37 * value in LANG, not with $LC_ALL, (b) internationalized
38 * LC_NUMERIC and LC_TIME are more PITA than benefit
39 * (for one, some utilities have hard time with comma
40 * used as a fractional separator).
41 */
42//TODO: avoid repeated calls by caching last string?
43 setlocale(LC_CTYPE, LANG ? LANG : "");
44
45 /* In unicode, this is a one character string */
46 width = unicode_strlen(unicode_0x394);
47 unicode_status = (width == 1 ? UNICODE_ON : UNICODE_OFF);
48}
49
50void FAST_FUNC init_unicode(void)
51{
52 /* Some people set only $LC_CTYPE, not $LC_ALL, because they want
53 * only Unicode to be activated on their system, not the whole
54 * shebang of wrong decimal points, strange date formats and so on.
55 */
56 if (unicode_status == UNICODE_UNKNOWN) {
57 char *s = getenv("LC_ALL");
58 if (!s) s = getenv("LC_CTYPE");
59 if (!s) s = getenv("LANG");
60 reinit_unicode(s);
61 }
62}
63
64#else
65
66/* Homegrown Unicode support. It knows only C and Unicode locales. */
67
68# if ENABLE_FEATURE_CHECK_UNICODE_IN_ENV
69void FAST_FUNC reinit_unicode(const char *LANG)
70{
71 unicode_status = UNICODE_OFF;
72 if (!LANG || !(strstr(LANG, ".utf") || strstr(LANG, ".UTF")))
73 return;
74 unicode_status = UNICODE_ON;
75}
76
77void FAST_FUNC init_unicode(void)
78{
79 if (unicode_status == UNICODE_UNKNOWN) {
80 char *s = getenv("LC_ALL");
81 if (!s) s = getenv("LC_CTYPE");
82 if (!s) s = getenv("LANG");
83 reinit_unicode(s);
84 }
85}
86# endif
87
88static size_t wcrtomb_internal(char *s, wchar_t wc)
89{
90 int n, i;
91 uint32_t v = wc;
92
93 if (v <= 0x7f) {
94 *s = v;
95 return 1;
96 }
97
98 /* RFC 3629 says that Unicode ends at 10FFFF,
99 * but we cover entire 32 bits */
100
101 /* 4000000-FFFFFFFF -> 111111tt 10tttttt 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx */
102 /* 200000-3FFFFFF -> 111110tt 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx */
103 /* 10000-1FFFFF -> 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx */
104 /* 800-FFFF -> 1110yyyy 10yyyyxx 10xxxxxx */
105 /* 80-7FF -> 110yyyxx 10xxxxxx */
106
107 /* How many bytes do we need? */
108 n = 2;
109 /* (0x80000000+ would result in n = 7, limiting n to 6) */
110 while (v >= 0x800 && n < 6) {
111 v >>= 5;
112 n++;
113 }
114 /* Fill bytes n-1..1 */
115 i = n;
116 while (--i) {
117 s[i] = (wc & 0x3f) | 0x80;
118 wc >>= 6;
119 }
120 /* Fill byte 0 */
121 s[0] = wc | (uint8_t)(0x3f00 >> n);
122 return n;
123}
124size_t FAST_FUNC wcrtomb(char *s, wchar_t wc, mbstate_t *ps UNUSED_PARAM)
125{
126 if (unicode_status != UNICODE_ON) {
127 *s = wc;
128 return 1;
129 }
130
131 return wcrtomb_internal(s, wc);
132}
133size_t FAST_FUNC wcstombs(char *dest, const wchar_t *src, size_t n)
134{
135 size_t org_n = n;
136
137 if (unicode_status != UNICODE_ON) {
138 while (n) {
139 wchar_t c = *src++;
140 *dest++ = c;
141 if (c == 0)
142 break;
143 n--;
144 }
145 return org_n - n;
146 }
147
148 while (n >= MB_CUR_MAX) {
149 wchar_t wc = *src++;
150 size_t len = wcrtomb_internal(dest, wc);
151
152 if (wc == L'\0')
153 return org_n - n;
154 dest += len;
155 n -= len;
156 }
157 while (n) {
158 char tbuf[MB_CUR_MAX];
159 wchar_t wc = *src++;
160 size_t len = wcrtomb_internal(tbuf, wc);
161
162 if (len > n)
163 break;
164 memcpy(dest, tbuf, len);
165 if (wc == L'\0')
166 return org_n - n;
167 dest += len;
168 n -= len;
169 }
170 return org_n - n;
171}
172
173# define ERROR_WCHAR (~(wchar_t)0)
174
175static const char *mbstowc_internal(wchar_t *res, const char *src)
176{
177 int bytes;
178 unsigned c = (unsigned char) *src++;
179
180 if (c <= 0x7f) {
181 *res = c;
182 return src;
183 }
184
185 /* 80-7FF -> 110yyyxx 10xxxxxx */
186 /* 800-FFFF -> 1110yyyy 10yyyyxx 10xxxxxx */
187 /* 10000-1FFFFF -> 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx */
188 /* 200000-3FFFFFF -> 111110tt 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx */
189 /* 4000000-FFFFFFFF -> 111111tt 10tttttt 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx */
190 bytes = 0;
191 do {
192 c <<= 1;
193 bytes++;
194 } while ((c & 0x80) && bytes < 6);
195 if (bytes == 1) {
196 /* A bare "continuation" byte. Say, 80 */
197 *res = ERROR_WCHAR;
198 return src;
199 }
200 c = (uint8_t)(c) >> bytes;
201
202 while (--bytes) {
203 unsigned ch = (unsigned char) *src;
204 if ((ch & 0xc0) != 0x80) {
205 /* Missing "continuation" byte. Example: e0 80 */
206 *res = ERROR_WCHAR;
207 return src;
208 }
209 c = (c << 6) + (ch & 0x3f);
210 src++;
211 }
212
213 /* TODO */
214 /* Need to check that c isn't produced by overlong encoding */
215 /* Example: 11000000 10000000 converts to NUL */
216 /* 11110000 10000000 10000100 10000000 converts to 0x100 */
217 /* correct encoding: 11000100 10000000 */
218 if (c <= 0x7f) { /* crude check */
219 *res = ERROR_WCHAR;
220 return src;
221 }
222
223 *res = c;
224 return src;
225}
226size_t FAST_FUNC mbstowcs(wchar_t *dest, const char *src, size_t n)
227{
228 size_t org_n = n;
229
230 if (unicode_status != UNICODE_ON) {
231 while (n) {
232 unsigned char c = *src++;
233
234 if (dest)
235 *dest++ = c;
236 if (c == 0)
237 break;
238 n--;
239 }
240 return org_n - n;
241 }
242
243 while (n) {
244 wchar_t wc;
245 src = mbstowc_internal(&wc, src);
246 if (wc == ERROR_WCHAR) /* error */
247 return (size_t) -1L;
248 if (dest)
249 *dest++ = wc;
250 if (wc == 0) /* end-of-string */
251 break;
252 n--;
253 }
254
255 return org_n - n;
256}
257
258int FAST_FUNC iswspace(wint_t wc)
259{
260 return (unsigned)wc <= 0x7f && isspace(wc);
261}
262
263int FAST_FUNC iswalnum(wint_t wc)
264{
265 return (unsigned)wc <= 0x7f && isalnum(wc);
266}
267
268int FAST_FUNC iswpunct(wint_t wc)
269{
270 return (unsigned)wc <= 0x7f && ispunct(wc);
271}
272
273
274# if CONFIG_LAST_SUPPORTED_WCHAR >= 0x300
275struct interval {
276 uint16_t first;
277 uint16_t last;
278};
279
280/* auxiliary function for binary search in interval table */
281static int in_interval_table(unsigned ucs, const struct interval *table, unsigned max)
282{
283 unsigned min;
284 unsigned mid;
285
286 if (ucs < table[0].first || ucs > table[max].last)
287 return 0;
288
289 min = 0;
290 while (max >= min) {
291 mid = (min + max) / 2;
292 if (ucs > table[mid].last)
293 min = mid + 1;
294 else if (ucs < table[mid].first)
295 max = mid - 1;
296 else
297 return 1;
298 }
299 return 0;
300}
301
302static int in_uint16_table(unsigned ucs, const uint16_t *table, unsigned max)
303{
304 unsigned min;
305 unsigned mid;
306 unsigned first, last;
307
308 first = table[0] >> 2;
309 last = first + (table[0] & 3);
310 if (ucs < first || ucs > last)
311 return 0;
312
313 min = 0;
314 while (max >= min) {
315 mid = (min + max) / 2;
316 first = table[mid] >> 2;
317 last = first + (table[mid] & 3);
318 if (ucs > last)
319 min = mid + 1;
320 else if (ucs < first)
321 max = mid - 1;
322 else
323 return 1;
324 }
325 return 0;
326}
327# endif
328
329
330/*
331 * This is an implementation of wcwidth() and wcswidth() (defined in
332 * IEEE Std 1002.1-2001) for Unicode.
333 *
334 * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
335 * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html
336 *
337 * In fixed-width output devices, Latin characters all occupy a single
338 * "cell" position of equal width, whereas ideographic CJK characters
339 * occupy two such cells. Interoperability between terminal-line
340 * applications and (teletype-style) character terminals using the
341 * UTF-8 encoding requires agreement on which character should advance
342 * the cursor by how many cell positions. No established formal
343 * standards exist at present on which Unicode character shall occupy
344 * how many cell positions on character terminals. These routines are
345 * a first attempt of defining such behavior based on simple rules
346 * applied to data provided by the Unicode Consortium.
347 *
348 * For some graphical characters, the Unicode standard explicitly
349 * defines a character-cell width via the definition of the East Asian
350 * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
351 * In all these cases, there is no ambiguity about which width a
352 * terminal shall use. For characters in the East Asian Ambiguous (A)
353 * class, the width choice depends purely on a preference of backward
354 * compatibility with either historic CJK or Western practice.
355 * Choosing single-width for these characters is easy to justify as
356 * the appropriate long-term solution, as the CJK practice of
357 * displaying these characters as double-width comes from historic
358 * implementation simplicity (8-bit encoded characters were displayed
359 * single-width and 16-bit ones double-width, even for Greek,
360 * Cyrillic, etc.) and not any typographic considerations.
361 *
362 * Much less clear is the choice of width for the Not East Asian
363 * (Neutral) class. Existing practice does not dictate a width for any
364 * of these characters. It would nevertheless make sense
365 * typographically to allocate two character cells to characters such
366 * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
367 * represented adequately with a single-width glyph. The following
368 * routines at present merely assign a single-cell width to all
369 * neutral characters, in the interest of simplicity. This is not
370 * entirely satisfactory and should be reconsidered before
371 * establishing a formal standard in this area. At the moment, the
372 * decision which Not East Asian (Neutral) characters should be
373 * represented by double-width glyphs cannot yet be answered by
374 * applying a simple rule from the Unicode database content. Setting
375 * up a proper standard for the behavior of UTF-8 character terminals
376 * will require a careful analysis not only of each Unicode character,
377 * but also of each presentation form, something the author of these
378 * routines has avoided to do so far.
379 *
380 * http://www.unicode.org/unicode/reports/tr11/
381 *
382 * Markus Kuhn -- 2007-05-26 (Unicode 5.0)
383 *
384 * Permission to use, copy, modify, and distribute this software
385 * for any purpose and without fee is hereby granted. The author
386 * disclaims all warranties with regard to this software.
387 *
388 * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
389 */
390
391/* Assigned Unicode character ranges:
392 * Plane Range
393 * 0 0000–FFFF Basic Multilingual Plane
394 * 1 10000–1FFFF Supplementary Multilingual Plane
395 * 2 20000–2FFFF Supplementary Ideographic Plane
396 * 3 30000-3FFFF Tertiary Ideographic Plane (no chars assigned yet)
397 * 4-13 40000–DFFFF currently unassigned
398 * 14 E0000–EFFFF Supplementary Special-purpose Plane
399 * 15 F0000–FFFFF Supplementary Private Use Area-A
400 * 16 100000–10FFFF Supplementary Private Use Area-B
401 *
402 * "Supplementary Special-purpose Plane currently contains non-graphical
403 * characters in two blocks of 128 and 240 characters. The first block
404 * is for language tag characters for use when language cannot be indicated
405 * through other protocols (such as the xml:lang attribute in XML).
406 * The other block contains glyph variation selectors to indicate
407 * an alternate glyph for a character that cannot be determined by context."
408 *
409 * In simpler terms: it is a tool to fix the "Han unification" mess
410 * created by Unicode committee, to select Chinese/Japanese/Korean/Taiwan
411 * version of a character. (They forgot that the whole purpose of the Unicode
412 * was to be able to write all chars in one charset without such tricks).
413 * Until East Asian users say it is actually necessary to support these
414 * code points in console applications like busybox
415 * (i.e. do these chars ever appear in filenames, hostnames, text files
416 * and such?), we are treating these code points as invalid.
417 *
418 * Tertiary Ideographic Plane is also ignored for now,
419 * until Unicode committee assigns something there.
420 */
421/* The following two functions define the column width of an ISO 10646
422 * character as follows:
423 *
424 * - The null character (U+0000) has a column width of 0.
425 *
426 * - Other C0/C1 control characters and DEL will lead to a return
427 * value of -1.
428 *
429 * - Non-spacing and enclosing combining characters (general
430 * category code Mn or Me in the Unicode database) have a
431 * column width of 0.
432 *
433 * - SOFT HYPHEN (U+00AD) has a column width of 1.
434 *
435 * - Other format characters (general category code Cf in the Unicode
436 * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
437 *
438 * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
439 * have a column width of 0.
440 *
441 * - Spacing characters in the East Asian Wide (W) or East Asian
442 * Full-width (F) category as defined in Unicode Technical
443 * Report #11 have a column width of 2.
444 *
445 * - All remaining characters (including all printable
446 * ISO 8859-1 and WGL4 characters, Unicode control characters,
447 * etc.) have a column width of 1.
448 *
449 * This implementation assumes that wchar_t characters are encoded
450 * in ISO 10646.
451 */
452int FAST_FUNC wcwidth(unsigned ucs)
453{
454# if CONFIG_LAST_SUPPORTED_WCHAR >= 0x300
455 /* sorted list of non-overlapping intervals of non-spacing characters */
456 /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
457# define BIG_(a,b) { a, b },
458# define PAIR(a,b)
459# define ARRAY /* PAIR if < 0x4000 and no more than 4 chars big */ \
460 BIG_(0x0300, 0x036F) \
461 PAIR(0x0483, 0x0486) \
462 PAIR(0x0488, 0x0489) \
463 BIG_(0x0591, 0x05BD) \
464 PAIR(0x05BF, 0x05BF) \
465 PAIR(0x05C1, 0x05C2) \
466 PAIR(0x05C4, 0x05C5) \
467 PAIR(0x05C7, 0x05C7) \
468 PAIR(0x0600, 0x0603) \
469 BIG_(0x0610, 0x0615) \
470 BIG_(0x064B, 0x065E) \
471 PAIR(0x0670, 0x0670) \
472 BIG_(0x06D6, 0x06E4) \
473 PAIR(0x06E7, 0x06E8) \
474 PAIR(0x06EA, 0x06ED) \
475 PAIR(0x070F, 0x070F) \
476 PAIR(0x0711, 0x0711) \
477 BIG_(0x0730, 0x074A) \
478 BIG_(0x07A6, 0x07B0) \
479 BIG_(0x07EB, 0x07F3) \
480 PAIR(0x0901, 0x0902) \
481 PAIR(0x093C, 0x093C) \
482 BIG_(0x0941, 0x0948) \
483 PAIR(0x094D, 0x094D) \
484 PAIR(0x0951, 0x0954) \
485 PAIR(0x0962, 0x0963) \
486 PAIR(0x0981, 0x0981) \
487 PAIR(0x09BC, 0x09BC) \
488 PAIR(0x09C1, 0x09C4) \
489 PAIR(0x09CD, 0x09CD) \
490 PAIR(0x09E2, 0x09E3) \
491 PAIR(0x0A01, 0x0A02) \
492 PAIR(0x0A3C, 0x0A3C) \
493 PAIR(0x0A41, 0x0A42) \
494 PAIR(0x0A47, 0x0A48) \
495 PAIR(0x0A4B, 0x0A4D) \
496 PAIR(0x0A70, 0x0A71) \
497 PAIR(0x0A81, 0x0A82) \
498 PAIR(0x0ABC, 0x0ABC) \
499 BIG_(0x0AC1, 0x0AC5) \
500 PAIR(0x0AC7, 0x0AC8) \
501 PAIR(0x0ACD, 0x0ACD) \
502 PAIR(0x0AE2, 0x0AE3) \
503 PAIR(0x0B01, 0x0B01) \
504 PAIR(0x0B3C, 0x0B3C) \
505 PAIR(0x0B3F, 0x0B3F) \
506 PAIR(0x0B41, 0x0B43) \
507 PAIR(0x0B4D, 0x0B4D) \
508 PAIR(0x0B56, 0x0B56) \
509 PAIR(0x0B82, 0x0B82) \
510 PAIR(0x0BC0, 0x0BC0) \
511 PAIR(0x0BCD, 0x0BCD) \
512 PAIR(0x0C3E, 0x0C40) \
513 PAIR(0x0C46, 0x0C48) \
514 PAIR(0x0C4A, 0x0C4D) \
515 PAIR(0x0C55, 0x0C56) \
516 PAIR(0x0CBC, 0x0CBC) \
517 PAIR(0x0CBF, 0x0CBF) \
518 PAIR(0x0CC6, 0x0CC6) \
519 PAIR(0x0CCC, 0x0CCD) \
520 PAIR(0x0CE2, 0x0CE3) \
521 PAIR(0x0D41, 0x0D43) \
522 PAIR(0x0D4D, 0x0D4D) \
523 PAIR(0x0DCA, 0x0DCA) \
524 PAIR(0x0DD2, 0x0DD4) \
525 PAIR(0x0DD6, 0x0DD6) \
526 PAIR(0x0E31, 0x0E31) \
527 BIG_(0x0E34, 0x0E3A) \
528 BIG_(0x0E47, 0x0E4E) \
529 PAIR(0x0EB1, 0x0EB1) \
530 BIG_(0x0EB4, 0x0EB9) \
531 PAIR(0x0EBB, 0x0EBC) \
532 BIG_(0x0EC8, 0x0ECD) \
533 PAIR(0x0F18, 0x0F19) \
534 PAIR(0x0F35, 0x0F35) \
535 PAIR(0x0F37, 0x0F37) \
536 PAIR(0x0F39, 0x0F39) \
537 BIG_(0x0F71, 0x0F7E) \
538 BIG_(0x0F80, 0x0F84) \
539 PAIR(0x0F86, 0x0F87) \
540 PAIR(0x0FC6, 0x0FC6) \
541 BIG_(0x0F90, 0x0F97) \
542 BIG_(0x0F99, 0x0FBC) \
543 PAIR(0x102D, 0x1030) \
544 PAIR(0x1032, 0x1032) \
545 PAIR(0x1036, 0x1037) \
546 PAIR(0x1039, 0x1039) \
547 PAIR(0x1058, 0x1059) \
548 BIG_(0x1160, 0x11FF) \
549 PAIR(0x135F, 0x135F) \
550 PAIR(0x1712, 0x1714) \
551 PAIR(0x1732, 0x1734) \
552 PAIR(0x1752, 0x1753) \
553 PAIR(0x1772, 0x1773) \
554 PAIR(0x17B4, 0x17B5) \
555 BIG_(0x17B7, 0x17BD) \
556 PAIR(0x17C6, 0x17C6) \
557 BIG_(0x17C9, 0x17D3) \
558 PAIR(0x17DD, 0x17DD) \
559 PAIR(0x180B, 0x180D) \
560 PAIR(0x18A9, 0x18A9) \
561 PAIR(0x1920, 0x1922) \
562 PAIR(0x1927, 0x1928) \
563 PAIR(0x1932, 0x1932) \
564 PAIR(0x1939, 0x193B) \
565 PAIR(0x1A17, 0x1A18) \
566 PAIR(0x1B00, 0x1B03) \
567 PAIR(0x1B34, 0x1B34) \
568 BIG_(0x1B36, 0x1B3A) \
569 PAIR(0x1B3C, 0x1B3C) \
570 PAIR(0x1B42, 0x1B42) \
571 BIG_(0x1B6B, 0x1B73) \
572 BIG_(0x1DC0, 0x1DCA) \
573 PAIR(0x1DFE, 0x1DFF) \
574 BIG_(0x200B, 0x200F) \
575 BIG_(0x202A, 0x202E) \
576 PAIR(0x2060, 0x2063) \
577 BIG_(0x206A, 0x206F) \
578 BIG_(0x20D0, 0x20EF) \
579 BIG_(0x302A, 0x302F) \
580 PAIR(0x3099, 0x309A) \
581 /* Too big to be packed in PAIRs: */ \
582 BIG_(0xA806, 0xA806) \
583 BIG_(0xA80B, 0xA80B) \
584 BIG_(0xA825, 0xA826) \
585 BIG_(0xFB1E, 0xFB1E) \
586 BIG_(0xFE00, 0xFE0F) \
587 BIG_(0xFE20, 0xFE23) \
588 BIG_(0xFEFF, 0xFEFF) \
589 BIG_(0xFFF9, 0xFFFB)
590 static const struct interval combining[] = { ARRAY };
591# undef BIG_
592# undef PAIR
593# define BIG_(a,b)
594# define PAIR(a,b) (a << 2) | (b-a),
595 static const uint16_t combining1[] = { ARRAY };
596# undef BIG_
597# undef PAIR
598# define BIG_(a,b) char big_##a[b < 0x4000 && b-a <= 3 ? -1 : 1];
599# define PAIR(a,b) char pair##a[b >= 0x4000 || b-a > 3 ? -1 : 1];
600 struct CHECK { ARRAY };
601# undef BIG_
602# undef PAIR
603# undef ARRAY
604# endif
605
606 if (ucs == 0)
607 return 0;
608
609 /* Test for 8-bit control characters (00-1f, 80-9f, 7f) */
610 if ((ucs & ~0x80) < 0x20 || ucs == 0x7f)
611 return -1;
612 /* Quick abort if it is an obviously invalid char */
613 if (ucs > CONFIG_LAST_SUPPORTED_WCHAR)
614 return -1;
615
616 /* Optimization: no combining chars below 0x300 */
617 if (CONFIG_LAST_SUPPORTED_WCHAR < 0x300 || ucs < 0x300)
618 return 1;
619
620# if CONFIG_LAST_SUPPORTED_WCHAR >= 0x300
621 /* Binary search in table of non-spacing characters */
622 if (in_interval_table(ucs, combining, ARRAY_SIZE(combining) - 1))
623 return 0;
624 if (in_uint16_table(ucs, combining1, ARRAY_SIZE(combining1) - 1))
625 return 0;
626
627 /* Optimization: all chars below 0x1100 are not double-width */
628 if (CONFIG_LAST_SUPPORTED_WCHAR < 0x1100 || ucs < 0x1100)
629 return 1;
630
631# if CONFIG_LAST_SUPPORTED_WCHAR >= 0x1100
632 /* Invalid code points: */
633 /* High (d800..dbff) and low (dc00..dfff) surrogates (valid only in UTF16) */
634 /* Private Use Area (e000..f8ff) */
635 /* Noncharacters fdd0..fdef */
636 if ((CONFIG_LAST_SUPPORTED_WCHAR >= 0xd800 && ucs >= 0xd800 && ucs <= 0xf8ff)
637 || (CONFIG_LAST_SUPPORTED_WCHAR >= 0xfdd0 && ucs >= 0xfdd0 && ucs <= 0xfdef)
638 ) {
639 return -1;
640 }
641 /* 0xfffe and 0xffff in every plane are invalid */
642 if (CONFIG_LAST_SUPPORTED_WCHAR >= 0xfffe && ((ucs & 0xfffe) == 0xfffe)) {
643 return -1;
644 }
645
646# if CONFIG_LAST_SUPPORTED_WCHAR >= 0x10000
647 if (ucs >= 0x10000) {
648 /* Combining chars in Supplementary Multilingual Plane 0x1xxxx */
649 static const struct interval combining0x10000[] = {
650 { 0x0A01, 0x0A03 }, { 0x0A05, 0x0A06 }, { 0x0A0C, 0x0A0F },
651 { 0x0A38, 0x0A3A }, { 0x0A3F, 0x0A3F }, { 0xD167, 0xD169 },
652 { 0xD173, 0xD182 }, { 0xD185, 0xD18B }, { 0xD1AA, 0xD1AD },
653 { 0xD242, 0xD244 }
654 };
655 /* Binary search in table of non-spacing characters in Supplementary Multilingual Plane */
656 if (in_interval_table(ucs ^ 0x10000, combining0x10000, ARRAY_SIZE(combining0x10000) - 1))
657 return 0;
658 /* Check a few non-spacing chars in Supplementary Special-purpose Plane 0xExxxx */
659 if (CONFIG_LAST_SUPPORTED_WCHAR >= 0xE0001
660 && ( ucs == 0xE0001
661 || (ucs >= 0xE0020 && ucs <= 0xE007F)
662 || (ucs >= 0xE0100 && ucs <= 0xE01EF)
663 )
664 ) {
665 return 0;
666 }
667 }
668# endif
669
670 /* If we arrive here, ucs is not a combining or C0/C1 control character.
671 * Check whether it's 1 char or 2-shar wide.
672 */
673 return 1 +
674 ( (/*ucs >= 0x1100 &&*/ ucs <= 0x115f) /* Hangul Jamo init. consonants */
675 || ucs == 0x2329 /* left-pointing angle bracket; also CJK punct. char */
676 || ucs == 0x232a /* right-pointing angle bracket; also CJK punct. char */
677 || (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs != 0x303f) /* CJK ... Yi */
678# if CONFIG_LAST_SUPPORTED_WCHAR >= 0xac00
679 || (ucs >= 0xac00 && ucs <= 0xd7a3) /* Hangul Syllables */
680 || (ucs >= 0xf900 && ucs <= 0xfaff) /* CJK Compatibility Ideographs */
681 || (ucs >= 0xfe10 && ucs <= 0xfe19) /* Vertical forms */
682 || (ucs >= 0xfe30 && ucs <= 0xfe6f) /* CJK Compatibility Forms */
683 || (ucs >= 0xff00 && ucs <= 0xff60) /* Fullwidth Forms */
684 || (ucs >= 0xffe0 && ucs <= 0xffe6)
685 || ((ucs >> 17) == (2 >> 1)) /* 20000..3ffff: Supplementary and Tertiary Ideographic Planes */
686# endif
687 );
688# endif /* >= 0x1100 */
689# endif /* >= 0x300 */
690}
691
692
693# if ENABLE_UNICODE_BIDI_SUPPORT
694int FAST_FUNC unicode_bidi_isrtl(wint_t wc)
695{
696 /* ranges taken from
697 * http://www.unicode.org/Public/5.2.0/ucd/extracted/DerivedBidiClass.txt
698 * Bidi_Class=Left_To_Right | Bidi_Class=Arabic_Letter
699 */
700# define BIG_(a,b) { a, b },
701# define PAIR(a,b)
702# define ARRAY \
703 PAIR(0x0590, 0x0590) \
704 PAIR(0x05BE, 0x05BE) \
705 PAIR(0x05C0, 0x05C0) \
706 PAIR(0x05C3, 0x05C3) \
707 PAIR(0x05C6, 0x05C6) \
708 BIG_(0x05C8, 0x05FF) \
709 PAIR(0x0604, 0x0605) \
710 PAIR(0x0608, 0x0608) \
711 PAIR(0x060B, 0x060B) \
712 PAIR(0x060D, 0x060D) \
713 BIG_(0x061B, 0x064A) \
714 PAIR(0x065F, 0x065F) \
715 PAIR(0x066D, 0x066F) \
716 BIG_(0x0671, 0x06D5) \
717 PAIR(0x06E5, 0x06E6) \
718 PAIR(0x06EE, 0x06EF) \
719 BIG_(0x06FA, 0x070E) \
720 PAIR(0x0710, 0x0710) \
721 BIG_(0x0712, 0x072F) \
722 BIG_(0x074B, 0x07A5) \
723 BIG_(0x07B1, 0x07EA) \
724 PAIR(0x07F4, 0x07F5) \
725 BIG_(0x07FA, 0x0815) \
726 PAIR(0x081A, 0x081A) \
727 PAIR(0x0824, 0x0824) \
728 PAIR(0x0828, 0x0828) \
729 BIG_(0x082E, 0x08FF) \
730 PAIR(0x200F, 0x200F) \
731 PAIR(0x202B, 0x202B) \
732 PAIR(0x202E, 0x202E) \
733 BIG_(0xFB1D, 0xFB1D) \
734 BIG_(0xFB1F, 0xFB28) \
735 BIG_(0xFB2A, 0xFD3D) \
736 BIG_(0xFD40, 0xFDCF) \
737 BIG_(0xFDC8, 0xFDCF) \
738 BIG_(0xFDF0, 0xFDFC) \
739 BIG_(0xFDFE, 0xFDFF) \
740 BIG_(0xFE70, 0xFEFE)
741 /* Probably not necessary
742 {0x10800, 0x1091E},
743 {0x10920, 0x10A00},
744 {0x10A04, 0x10A04},
745 {0x10A07, 0x10A0B},
746 {0x10A10, 0x10A37},
747 {0x10A3B, 0x10A3E},
748 {0x10A40, 0x10A7F},
749 {0x10B36, 0x10B38},
750 {0x10B40, 0x10E5F},
751 {0x10E7F, 0x10FFF},
752 {0x1E800, 0x1EFFF}
753 */
754 static const struct interval rtl_b[] = { ARRAY };
755# undef BIG_
756# undef PAIR
757# define BIG_(a,b)
758# define PAIR(a,b) (a << 2) | (b-a),
759 static const uint16_t rtl_p[] = { ARRAY };
760# undef BIG_
761# undef PAIR
762# define BIG_(a,b) char big_##a[b < 0x4000 && b-a <= 3 ? -1 : 1];
763# define PAIR(a,b) char pair##a[b >= 0x4000 || b-a > 3 ? -1 : 1];
764 struct CHECK { ARRAY };
765# undef BIG_
766# undef PAIR
767# undef ARRAY
768
769 if (in_interval_table(wc, rtl_b, ARRAY_SIZE(rtl_b) - 1))
770 return 1;
771 if (in_uint16_table(wc, rtl_p, ARRAY_SIZE(rtl_p) - 1))
772 return 1;
773 return 0;
774}
775
776# if ENABLE_UNICODE_NEUTRAL_TABLE
777int FAST_FUNC unicode_bidi_is_neutral_wchar(wint_t wc)
778{
779 /* ranges taken from
780 * http://www.unicode.org/Public/5.2.0/ucd/extracted/DerivedBidiClass.txt
781 * Bidi_Classes: Paragraph_Separator, Segment_Separator,
782 * White_Space, Other_Neutral, European_Number, European_Separator,
783 * European_Terminator, Arabic_Number, Common_Separator
784 */
785# define BIG_(a,b) { a, b },
786# define PAIR(a,b)
787# define ARRAY \
788 BIG_(0x0009, 0x000D) \
789 BIG_(0x001C, 0x0040) \
790 BIG_(0x005B, 0x0060) \
791 PAIR(0x007B, 0x007E) \
792 PAIR(0x0085, 0x0085) \
793 BIG_(0x00A0, 0x00A9) \
794 PAIR(0x00AB, 0x00AC) \
795 BIG_(0x00AE, 0x00B4) \
796 PAIR(0x00B6, 0x00B9) \
797 BIG_(0x00BB, 0x00BF) \
798 PAIR(0x00D7, 0x00D7) \
799 PAIR(0x00F7, 0x00F7) \
800 PAIR(0x02B9, 0x02BA) \
801 BIG_(0x02C2, 0x02CF) \
802 BIG_(0x02D2, 0x02DF) \
803 BIG_(0x02E5, 0x02FF) \
804 PAIR(0x0374, 0x0375) \
805 PAIR(0x037E, 0x037E) \
806 PAIR(0x0384, 0x0385) \
807 PAIR(0x0387, 0x0387) \
808 PAIR(0x03F6, 0x03F6) \
809 PAIR(0x058A, 0x058A) \
810 PAIR(0x0600, 0x0603) \
811 PAIR(0x0606, 0x0607) \
812 PAIR(0x0609, 0x060A) \
813 PAIR(0x060C, 0x060C) \
814 PAIR(0x060E, 0x060F) \
815 BIG_(0x0660, 0x066C) \
816 PAIR(0x06DD, 0x06DD) \
817 PAIR(0x06E9, 0x06E9) \
818 BIG_(0x06F0, 0x06F9) \
819 PAIR(0x07F6, 0x07F9) \
820 PAIR(0x09F2, 0x09F3) \
821 PAIR(0x09FB, 0x09FB) \
822 PAIR(0x0AF1, 0x0AF1) \
823 BIG_(0x0BF3, 0x0BFA) \
824 BIG_(0x0C78, 0x0C7E) \
825 PAIR(0x0CF1, 0x0CF2) \
826 PAIR(0x0E3F, 0x0E3F) \
827 PAIR(0x0F3A, 0x0F3D) \
828 BIG_(0x1390, 0x1400) \
829 PAIR(0x1680, 0x1680) \
830 PAIR(0x169B, 0x169C) \
831 PAIR(0x17DB, 0x17DB) \
832 BIG_(0x17F0, 0x17F9) \
833 BIG_(0x1800, 0x180A) \
834 PAIR(0x180E, 0x180E) \
835 PAIR(0x1940, 0x1940) \
836 PAIR(0x1944, 0x1945) \
837 BIG_(0x19DE, 0x19FF) \
838 PAIR(0x1FBD, 0x1FBD) \
839 PAIR(0x1FBF, 0x1FC1) \
840 PAIR(0x1FCD, 0x1FCF) \
841 PAIR(0x1FDD, 0x1FDF) \
842 PAIR(0x1FED, 0x1FEF) \
843 PAIR(0x1FFD, 0x1FFE) \
844 BIG_(0x2000, 0x200A) \
845 BIG_(0x2010, 0x2029) \
846 BIG_(0x202F, 0x205F) \
847 PAIR(0x2070, 0x2070) \
848 BIG_(0x2074, 0x207E) \
849 BIG_(0x2080, 0x208E) \
850 BIG_(0x20A0, 0x20B8) \
851 PAIR(0x2100, 0x2101) \
852 PAIR(0x2103, 0x2106) \
853 PAIR(0x2108, 0x2109) \
854 PAIR(0x2114, 0x2114) \
855 PAIR(0x2116, 0x2118) \
856 BIG_(0x211E, 0x2123) \
857 PAIR(0x2125, 0x2125) \
858 PAIR(0x2127, 0x2127) \
859 PAIR(0x2129, 0x2129) \
860 PAIR(0x212E, 0x212E) \
861 PAIR(0x213A, 0x213B) \
862 BIG_(0x2140, 0x2144) \
863 PAIR(0x214A, 0x214D) \
864 BIG_(0x2150, 0x215F) \
865 PAIR(0x2189, 0x2189) \
866 BIG_(0x2190, 0x2335) \
867 BIG_(0x237B, 0x2394) \
868 BIG_(0x2396, 0x23E8) \
869 BIG_(0x2400, 0x2426) \
870 BIG_(0x2440, 0x244A) \
871 BIG_(0x2460, 0x249B) \
872 BIG_(0x24EA, 0x26AB) \
873 BIG_(0x26AD, 0x26CD) \
874 BIG_(0x26CF, 0x26E1) \
875 PAIR(0x26E3, 0x26E3) \
876 BIG_(0x26E8, 0x26FF) \
877 PAIR(0x2701, 0x2704) \
878 PAIR(0x2706, 0x2709) \
879 BIG_(0x270C, 0x2727) \
880 BIG_(0x2729, 0x274B) \
881 PAIR(0x274D, 0x274D) \
882 PAIR(0x274F, 0x2752) \
883 BIG_(0x2756, 0x275E) \
884 BIG_(0x2761, 0x2794) \
885 BIG_(0x2798, 0x27AF) \
886 BIG_(0x27B1, 0x27BE) \
887 BIG_(0x27C0, 0x27CA) \
888 PAIR(0x27CC, 0x27CC) \
889 BIG_(0x27D0, 0x27FF) \
890 BIG_(0x2900, 0x2B4C) \
891 BIG_(0x2B50, 0x2B59) \
892 BIG_(0x2CE5, 0x2CEA) \
893 BIG_(0x2CF9, 0x2CFF) \
894 BIG_(0x2E00, 0x2E99) \
895 BIG_(0x2E9B, 0x2EF3) \
896 BIG_(0x2F00, 0x2FD5) \
897 BIG_(0x2FF0, 0x2FFB) \
898 BIG_(0x3000, 0x3004) \
899 BIG_(0x3008, 0x3020) \
900 PAIR(0x3030, 0x3030) \
901 PAIR(0x3036, 0x3037) \
902 PAIR(0x303D, 0x303D) \
903 PAIR(0x303E, 0x303F) \
904 PAIR(0x309B, 0x309C) \
905 PAIR(0x30A0, 0x30A0) \
906 PAIR(0x30FB, 0x30FB) \
907 BIG_(0x31C0, 0x31E3) \
908 PAIR(0x321D, 0x321E) \
909 BIG_(0x3250, 0x325F) \
910 PAIR(0x327C, 0x327E) \
911 BIG_(0x32B1, 0x32BF) \
912 PAIR(0x32CC, 0x32CF) \
913 PAIR(0x3377, 0x337A) \
914 PAIR(0x33DE, 0x33DF) \
915 PAIR(0x33FF, 0x33FF) \
916 BIG_(0x4DC0, 0x4DFF) \
917 BIG_(0xA490, 0xA4C6) \
918 BIG_(0xA60D, 0xA60F) \
919 BIG_(0xA673, 0xA673) \
920 BIG_(0xA67E, 0xA67F) \
921 BIG_(0xA700, 0xA721) \
922 BIG_(0xA788, 0xA788) \
923 BIG_(0xA828, 0xA82B) \
924 BIG_(0xA838, 0xA839) \
925 BIG_(0xA874, 0xA877) \
926 BIG_(0xFB29, 0xFB29) \
927 BIG_(0xFD3E, 0xFD3F) \
928 BIG_(0xFDFD, 0xFDFD) \
929 BIG_(0xFE10, 0xFE19) \
930 BIG_(0xFE30, 0xFE52) \
931 BIG_(0xFE54, 0xFE66) \
932 BIG_(0xFE68, 0xFE6B) \
933 BIG_(0xFF01, 0xFF20) \
934 BIG_(0xFF3B, 0xFF40) \
935 BIG_(0xFF5B, 0xFF65) \
936 BIG_(0xFFE0, 0xFFE6) \
937 BIG_(0xFFE8, 0xFFEE) \
938 BIG_(0xFFF9, 0xFFFD)
939 /*
940 {0x10101, 0x10101},
941 {0x10140, 0x1019B},
942 {0x1091F, 0x1091F},
943 {0x10B39, 0x10B3F},
944 {0x10E60, 0x10E7E},
945 {0x1D200, 0x1D241},
946 {0x1D245, 0x1D245},
947 {0x1D300, 0x1D356},
948 {0x1D6DB, 0x1D6DB},
949 {0x1D715, 0x1D715},
950 {0x1D74F, 0x1D74F},
951 {0x1D789, 0x1D789},
952 {0x1D7C3, 0x1D7C3},
953 {0x1D7CE, 0x1D7FF},
954 {0x1F000, 0x1F02B},
955 {0x1F030, 0x1F093},
956 {0x1F100, 0x1F10A}
957 */
958 static const struct interval neutral_b[] = { ARRAY };
959# undef BIG_
960# undef PAIR
961# define BIG_(a,b)
962# define PAIR(a,b) (a << 2) | (b-a),
963 static const uint16_t neutral_p[] = { ARRAY };
964# undef BIG_
965# undef PAIR
966# define BIG_(a,b) char big_##a[b < 0x4000 && b-a <= 3 ? -1 : 1];
967# define PAIR(a,b) char pair##a[b >= 0x4000 || b-a > 3 ? -1 : 1];
968 struct CHECK { ARRAY };
969# undef BIG_
970# undef PAIR
971# undef ARRAY
972
973 if (in_interval_table(wc, neutral_b, ARRAY_SIZE(neutral_b) - 1))
974 return 1;
975 if (in_uint16_table(wc, neutral_p, ARRAY_SIZE(neutral_p) - 1))
976 return 1;
977 return 0;
978}
979# endif
980
981# endif /* UNICODE_BIDI_SUPPORT */
982
983#endif /* Homegrown Unicode support */
984
985
986/* The rest is mostly same for libc and for "homegrown" support */
987
988size_t FAST_FUNC unicode_strlen(const char *string)
989{
990 size_t width = mbstowcs(NULL, string, INT_MAX);
991 if (width == (size_t)-1L)
992 return strlen(string);
993 return width;
994}
995
996size_t FAST_FUNC unicode_strwidth(const char *string)
997{
998 uni_stat_t uni_stat;
999 printable_string(&uni_stat, string);
1000 return uni_stat.unicode_width;
1001}
1002
1003static char* FAST_FUNC unicode_conv_to_printable2(uni_stat_t *stats, const char *src, unsigned width, int flags)
1004{
1005 char *dst;
1006 unsigned dst_len;
1007 unsigned uni_count;
1008 unsigned uni_width;
1009
1010 if (unicode_status != UNICODE_ON) {
1011 char *d;
1012 if (flags & UNI_FLAG_PAD) {
1013 d = dst = xmalloc(width + 1);
1014 while ((int)--width >= 0) {
1015 unsigned char c = *src;
1016 if (c == '\0') {
1017 do
1018 *d++ = ' ';
1019 while ((int)--width >= 0);
1020 break;
1021 }
1022 *d++ = (c >= ' ' && c < 0x7f) ? c : '?';
1023 src++;
1024 }
1025 *d = '\0';
1026 } else {
1027 d = dst = xstrndup(src, width);
1028 while (*d) {
1029 unsigned char c = *d;
1030 if (c < ' ' || c >= 0x7f)
1031 *d = '?';
1032 d++;
1033 }
1034 }
1035 if (stats) {
1036 stats->byte_count = (d - dst);
1037 stats->unicode_count = (d - dst);
1038 stats->unicode_width = (d - dst);
1039 }
1040 return dst;
1041 }
1042
1043 dst = NULL;
1044 uni_count = uni_width = 0;
1045 dst_len = 0;
1046 while (1) {
1047 int w;
1048 wchar_t wc;
1049
1050#if ENABLE_UNICODE_USING_LOCALE
1051 {
1052 mbstate_t mbst = { 0 };
1053 ssize_t rc = mbsrtowcs(&wc, &src, 1, &mbst);
1054 /* If invalid sequence is seen: -1 is returned,
1055 * src points to the invalid sequence, errno = EILSEQ.
1056 * Else number of wchars (excluding terminating L'\0')
1057 * written to dest is returned.
1058 * If len (here: 1) non-L'\0' wchars stored at dest,
1059 * src points to the next char to be converted.
1060 * If string is completely converted: src = NULL.
1061 */
1062 if (rc == 0) /* end-of-string */
1063 break;
1064 if (rc < 0) { /* error */
1065 src++;
1066 goto subst;
1067 }
1068 if (!iswprint(wc))
1069 goto subst;
1070 }
1071#else
1072 src = mbstowc_internal(&wc, src);
1073 /* src is advanced to next mb char
1074 * wc == ERROR_WCHAR: invalid sequence is seen
1075 * else: wc is set
1076 */
1077 if (wc == ERROR_WCHAR) /* error */
1078 goto subst;
1079 if (wc == 0) /* end-of-string */
1080 break;
1081#endif
1082 if (CONFIG_LAST_SUPPORTED_WCHAR && wc > CONFIG_LAST_SUPPORTED_WCHAR)
1083 goto subst;
1084 w = wcwidth(wc);
1085 if ((ENABLE_UNICODE_COMBINING_WCHARS && w < 0) /* non-printable wchar */
1086 || (!ENABLE_UNICODE_COMBINING_WCHARS && w <= 0)
1087 || (!ENABLE_UNICODE_WIDE_WCHARS && w > 1)
1088 ) {
1089 subst:
1090 wc = CONFIG_SUBST_WCHAR;
1091 w = 1;
1092 }
1093 width -= w;
1094 /* Note: if width == 0, we still may add more chars,
1095 * they may be zero-width or combining ones */
1096 if ((int)width < 0) {
1097 /* can't add this wc, string would become longer than width */
1098 width += w;
1099 break;
1100 }
1101
1102 uni_count++;
1103 uni_width += w;
1104 dst = xrealloc(dst, dst_len + MB_CUR_MAX);
1105#if ENABLE_UNICODE_USING_LOCALE
1106 {
1107 mbstate_t mbst = { 0 };
1108 dst_len += wcrtomb(&dst[dst_len], wc, &mbst);
1109 }
1110#else
1111 dst_len += wcrtomb_internal(&dst[dst_len], wc);
1112#endif
1113 }
1114
1115 /* Pad to remaining width */
1116 if (flags & UNI_FLAG_PAD) {
1117 dst = xrealloc(dst, dst_len + width + 1);
1118 uni_count += width;
1119 uni_width += width;
1120 while ((int)--width >= 0) {
1121 dst[dst_len++] = ' ';
1122 }
1123 }
1124 dst[dst_len] = '\0';
1125 if (stats) {
1126 stats->byte_count = dst_len;
1127 stats->unicode_count = uni_count;
1128 stats->unicode_width = uni_width;
1129 }
1130
1131 return dst;
1132}
1133char* FAST_FUNC unicode_conv_to_printable(uni_stat_t *stats, const char *src)
1134{
1135 return unicode_conv_to_printable2(stats, src, INT_MAX, 0);
1136}
1137char* FAST_FUNC unicode_conv_to_printable_fixedwidth(/*uni_stat_t *stats,*/ const char *src, unsigned width)
1138{
1139 return unicode_conv_to_printable2(/*stats:*/ NULL, src, width, UNI_FLAG_PAD);
1140}
1141
1142#ifdef UNUSED
1143char* FAST_FUNC unicode_conv_to_printable_maxwidth(uni_stat_t *stats, const char *src, unsigned maxwidth)
1144{
1145 return unicode_conv_to_printable2(stats, src, maxwidth, 0);
1146}
1147
1148unsigned FAST_FUNC unicode_padding_to_width(unsigned width, const char *src)
1149{
1150 if (unicode_status != UNICODE_ON) {
1151 return width - strnlen(src, width);
1152 }
1153
1154 while (1) {
1155 int w;
1156 wchar_t wc;
1157
1158#if ENABLE_UNICODE_USING_LOCALE
1159 {
1160 mbstate_t mbst = { 0 };
1161 ssize_t rc = mbsrtowcs(&wc, &src, 1, &mbst);
1162 if (rc <= 0) /* error, or end-of-string */
1163 return width;
1164 }
1165#else
1166 src = mbstowc_internal(&wc, src);
1167 if (wc == ERROR_WCHAR || wc == 0) /* error, or end-of-string */
1168 return width;
1169#endif
1170 w = wcwidth(wc);
1171 if (w < 0) /* non-printable wchar */
1172 return width;
1173 width -= w;
1174 if ((int)width <= 0) /* string is longer than width */
1175 return 0;
1176 }
1177}
1178#endif
Note: See TracBrowser for help on using the repository browser.