source: trunk/libtransmission/ConvertUTF.c @ 8391

Last change on this file since 8391 was 8391, checked in by charles, 13 years ago

(trunk libT) fix small dead code. found by clang static analyzer and reported by d0k

File size: 20.1 KB
Line 
1/*
2 * Copyright 2001-2004 Unicode, Inc.
3 *
4 * Disclaimer
5 *
6 * This source code is provided as is by Unicode, Inc. No claims are
7 * made as to fitness for any particular purpose. No warranties of any
8 * kind are expressed or implied. The recipient agrees to determine
9 * applicability of information provided. If this file has been
10 * purchased on magnetic or optical media from Unicode, Inc., the
11 * sole remedy for any claim will be exchange of defective media
12 * within 90 days of receipt.
13 *
14 * Limitations on Rights to Redistribute This Code
15 *
16 * Unicode, Inc. hereby grants the right to freely use the information
17 * supplied in this file in the creation of products supporting the
18 * Unicode Standard, and to make copies of this file in any form
19 * for internal or external distribution as long as this notice
20 * remains attached.
21 */
22
23/* ---------------------------------------------------------------------
24
25    Conversions between UTF32, UTF-16, and UTF-8. Source code file.
26    Author: Mark E. Davis, 1994.
27    Rev History: Rick McGowan, fixes & updates May 2001.
28    Sept 2001: fixed const & error conditions per
29        mods suggested by S. Parent & A. Lillich.
30    June 2002: Tim Dodd added detection and handling of incomplete
31        source sequences, enhanced error detection, added casts
32        to eliminate compiler warnings.
33    July 2003: slight mods to back out aggressive FFFE detection.
34    Jan 2004: updated switches in from-UTF8 conversions.
35    Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions.
36    May 2006: updated isLegalUTF8Sequence.
37
38    See the header file "ConvertUTF.h" for complete documentation.
39
40------------------------------------------------------------------------ */
41
42#ifdef CVTUTF_DEBUG
43 #include <stdio.h>
44#endif
45#include <string.h> /* strlen() */
46#include "ConvertUTF.h"
47
48static const int halfShift  = 10; /* used for shifting by 10 bits */
49
50static const UTF32 halfBase = 0x0010000UL;
51static const UTF32 halfMask = 0x3FFUL;
52
53#define UNI_SUR_HIGH_START  (UTF32)0xD800
54#define UNI_SUR_HIGH_END    (UTF32)0xDBFF
55#define UNI_SUR_LOW_START   (UTF32)0xDC00
56#define UNI_SUR_LOW_END     (UTF32)0xDFFF
57#define false      0
58#define true        1
59
60/* --------------------------------------------------------------------- */
61
62ConversionResult ConvertUTF32toUTF16 (
63        const UTF32** sourceStart, const UTF32* sourceEnd, 
64        UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
65    ConversionResult result = conversionOK;
66    const UTF32* source = *sourceStart;
67    UTF16* target = *targetStart;
68    while (source < sourceEnd) {
69        UTF32 ch;
70        if (target >= targetEnd) {
71            result = targetExhausted; break;
72        }
73        ch = *source++;
74        if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
75            /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */
76            if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
77                if (flags == strictConversion) {
78                    --source; /* return to the illegal value itself */
79                    result = sourceIllegal;
80                    break;
81                } else {
82                    *target++ = UNI_REPLACEMENT_CHAR;
83                }
84            } else {
85                *target++ = (UTF16)ch; /* normal case */
86            }
87        } else if (ch > UNI_MAX_LEGAL_UTF32) {
88            if (flags == strictConversion) {
89                result = sourceIllegal;
90            } else {
91                *target++ = UNI_REPLACEMENT_CHAR;
92            }
93        } else {
94            /* target is a character in range 0xFFFF - 0x10FFFF. */
95            if (target + 1 >= targetEnd) {
96                --source; /* Back up source pointer! */
97                result = targetExhausted; break;
98            }
99            ch -= halfBase;
100            *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
101            *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
102        }
103    }
104    *sourceStart = source;
105    *targetStart = target;
106    return result;
107}
108
109/* --------------------------------------------------------------------- */
110
111ConversionResult ConvertUTF16toUTF32 (
112        const UTF16** sourceStart, const UTF16* sourceEnd, 
113        UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) {
114    ConversionResult result = conversionOK;
115    const UTF16* source = *sourceStart;
116    UTF32* target = *targetStart;
117    UTF32 ch, ch2;
118    while (source < sourceEnd) {
119        const UTF16* oldSource = source; /*  In case we have to back up because of target overflow. */
120        ch = *source++;
121        /* If we have a surrogate pair, convert to UTF32 first. */
122        if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
123            /* If the 16 bits following the high surrogate are in the source buffer... */
124            if (source < sourceEnd) {
125                ch2 = *source;
126                /* If it's a low surrogate, convert to UTF32. */
127                if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
128                    ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
129                        + (ch2 - UNI_SUR_LOW_START) + halfBase;
130                    ++source;
131                } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
132                    --source; /* return to the illegal value itself */
133                    result = sourceIllegal;
134                    break;
135                }
136            } else { /* We don't have the 16 bits following the high surrogate. */
137                --source; /* return to the high surrogate */
138                result = sourceExhausted;
139                break;
140            }
141        } else if (flags == strictConversion) {
142            /* UTF-16 surrogate values are illegal in UTF-32 */
143            if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
144                --source; /* return to the illegal value itself */
145                result = sourceIllegal;
146                break;
147            }
148        }
149        if (target >= targetEnd) {
150            source = oldSource; /* Back up source pointer! */
151            result = targetExhausted; break;
152        }
153        *target++ = ch;
154    }
155    *sourceStart = source;
156    *targetStart = target;
157#ifdef CVTUTF_DEBUG
158if (result == sourceIllegal) {
159    fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2);
160    fflush(stderr);
161}
162#endif
163    return result;
164}
165
166/* --------------------------------------------------------------------- */
167
168/*
169 * Index into the table below with the first byte of a UTF-8 sequence to
170 * get the number of trailing bytes that are supposed to follow it.
171 * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
172 * left as-is for anyone who may want to do such conversion, which was
173 * allowed in earlier algorithms.
174 */
175static const char trailingBytesForUTF8[256] = {
176    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
177    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
178    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
179    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
180    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
181    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
182    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
183    2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
184};
185
186/*
187 * Magic values subtracted from a buffer value during UTF8 conversion.
188 * This table contains as many values as there might be trailing bytes
189 * in a UTF-8 sequence.
190 */
191static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, 
192                     0x03C82080UL, 0xFA082080UL, 0x82082080UL };
193
194/*
195 * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
196 * into the first byte, depending on how many bytes follow.  There are
197 * as many entries in this table as there are UTF-8 sequence types.
198 * (I.e., one byte sequence, two byte... etc.). Remember that sequencs
199 * for *legal* UTF-8 will be 4 or fewer bytes total.
200 */
201static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
202
203/* --------------------------------------------------------------------- */
204
205/* The interface converts a whole buffer to avoid function-call overhead.
206 * Constants have been gathered. Loops & conditionals have been removed as
207 * much as possible for efficiency, in favor of drop-through switches.
208 * (See "Note A" at the bottom of the file for equivalent code.)
209 * If your compiler supports it, the "isLegalUTF8" call can be turned
210 * into an inline function.
211 */
212
213/* --------------------------------------------------------------------- */
214
215ConversionResult ConvertUTF16toUTF8 (
216        const UTF16** sourceStart, const UTF16* sourceEnd, 
217        UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
218    ConversionResult result = conversionOK;
219    const UTF16* source = *sourceStart;
220    UTF8* target = *targetStart;
221    while (source < sourceEnd) {
222        UTF32 ch;
223        unsigned short bytesToWrite = 0;
224        const UTF32 byteMask = 0xBF;
225        const UTF32 byteMark = 0x80; 
226        const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
227        ch = *source++;
228        /* If we have a surrogate pair, convert to UTF32 first. */
229        if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
230            /* If the 16 bits following the high surrogate are in the source buffer... */
231            if (source < sourceEnd) {
232                UTF32 ch2 = *source;
233                /* If it's a low surrogate, convert to UTF32. */
234                if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
235                    ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
236                        + (ch2 - UNI_SUR_LOW_START) + halfBase;
237                    ++source;
238                } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
239                    --source; /* return to the illegal value itself */
240                    result = sourceIllegal;
241                    break;
242                }
243            } else { /* We don't have the 16 bits following the high surrogate. */
244                --source; /* return to the high surrogate */
245                result = sourceExhausted;
246                break;
247            }
248        } else if (flags == strictConversion) {
249            /* UTF-16 surrogate values are illegal in UTF-32 */
250            if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
251                --source; /* return to the illegal value itself */
252                result = sourceIllegal;
253                break;
254            }
255        }
256        /* Figure out how many bytes the result will require */
257        if (ch < (UTF32)0x80) {      bytesToWrite = 1;
258        } else if (ch < (UTF32)0x800) {     bytesToWrite = 2;
259        } else if (ch < (UTF32)0x10000) {   bytesToWrite = 3;
260        } else if (ch < (UTF32)0x110000) {  bytesToWrite = 4;
261        } else {                            bytesToWrite = 3;
262                                            ch = UNI_REPLACEMENT_CHAR;
263        }
264
265        target += bytesToWrite;
266        if (target > targetEnd) {
267            source = oldSource; /* Back up source pointer! */
268            target -= bytesToWrite; result = targetExhausted; break;
269        }
270        switch (bytesToWrite) { /* note: everything falls through. */
271            case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
272            case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
273            case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
274            case 1: *--target =  (UTF8)(ch | firstByteMark[bytesToWrite]);
275        }
276        target += bytesToWrite;
277    }
278    *sourceStart = source;
279    *targetStart = target;
280    return result;
281}
282
283/* --------------------------------------------------------------------- */
284
285/*
286 * Utility routine to tell whether a sequence of bytes is legal UTF-8.
287 * This must be called with the length pre-determined by the first byte.
288 * If not calling this from ConvertUTF8to*, then the length can be set by:
289 *  length = trailingBytesForUTF8[*source]+1;
290 * and the sequence is illegal right away if there aren't that many bytes
291 * available.
292 * If presented with a length > 4, this returns false.  The Unicode
293 * definition of UTF-8 goes up to 4-byte sequences.
294 */
295
296static Boolean isLegalUTF8(const UTF8 *source, int length) {
297    UTF8 a;
298    const UTF8 *srcptr = source+length;
299    switch (length) {
300    default: return false;
301        /* Everything else falls through when "true"... */
302    case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
303    case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
304    case 2: if ((a = (*--srcptr)) > 0xBF) return false;
305
306        switch (*source) {
307            /* no fall-through in this inner switch */
308            case 0xE0: if (a < 0xA0) return false; break;
309            case 0xED: if ((a < 0x80) || (a > 0x9F)) return false; break;
310            case 0xF0: if (a < 0x90) return false; break;
311            case 0xF4: if (a > 0x8F) return false; break;
312            default:   if (a < 0x80) return false;
313        }
314
315    case 1: if (*source >= 0x80 && *source < 0xC2) return false;
316    }
317    if (*source > 0xF4) return false;
318    return true;
319}
320
321/* --------------------------------------------------------------------- */
322
323/*
324 * Exported function to return whether a UTF-8 sequence is legal or not.
325 * This is not used here; it's just exported.
326 */
327
328Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) {
329    int length;
330    if (source == sourceEnd) {
331        return true;
332    }
333    while (true) {
334        length = trailingBytesForUTF8[*source]+1;
335        if (source+length > sourceEnd) {
336            return false;
337        }
338        if (!isLegalUTF8(source, length)) {
339            return false;
340        }
341        source += length;
342        if (source >= sourceEnd) {
343            return true;
344        }
345    }
346}
347
348/**
349 * This is a variation of isLegalUTF8Sequence() that behaves like g_utf8_validate().
350 * In addition to knowing if the sequence is legal, it also tells you the last good character.
351 */
352Boolean
353tr_utf8_validate( const char * str, int max_len, const char ** end )
354{
355    const UTF8* source = (const UTF8*) str;
356    const UTF8* sourceEnd;
357
358    if( max_len == 0 )
359        return true;
360
361    if( str == NULL )
362        return false;
363
364    sourceEnd = source + ((max_len < 0) ? strlen(str) : (size_t)max_len);
365
366    if( source == sourceEnd )
367    {
368        if( end != NULL )
369            *end = (const char*) source;
370        return true;
371    }
372
373    for( ;; )
374    {
375        const int length = trailingBytesForUTF8[*source] + 1;
376        if (source + length > sourceEnd) {
377            if( end != NULL )
378                *end = (const char*) source;
379            return false;
380        }
381        if (!isLegalUTF8(source, length)) {
382            if( end != NULL )
383                *end = (const char*) source;
384            return false;
385        }
386        source += length;
387        if (source >= sourceEnd) {
388            if( end != NULL )
389                *end = (const char*) source;
390            return true;
391        }
392    }
393
394   
395}
396
397
398/* --------------------------------------------------------------------- */
399
400ConversionResult ConvertUTF8toUTF16 (
401        const UTF8** sourceStart, const UTF8* sourceEnd, 
402        UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
403    ConversionResult result = conversionOK;
404    const UTF8* source = *sourceStart;
405    UTF16* target = *targetStart;
406    while (source < sourceEnd) {
407        UTF32 ch = 0;
408        unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
409        if (source + extraBytesToRead >= sourceEnd) {
410            result = sourceExhausted; break;
411        }
412        /* Do this check whether lenient or strict */
413        if (! isLegalUTF8(source, extraBytesToRead+1)) {
414            result = sourceIllegal;
415            break;
416        }
417        /*
418         * The cases all fall through. See "Note A" below.
419         */
420        switch (extraBytesToRead) {
421            case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
422            case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
423            case 3: ch += *source++; ch <<= 6;
424            case 2: ch += *source++; ch <<= 6;
425            case 1: ch += *source++; ch <<= 6;
426            case 0: ch += *source++;
427        }
428        ch -= offsetsFromUTF8[extraBytesToRead];
429
430        if (target >= targetEnd) {
431            source -= (extraBytesToRead+1); /* Back up source pointer! */
432            result = targetExhausted; break;
433        }
434        if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
435            /* UTF-16 surrogate values are illegal in UTF-32 */
436            if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
437                if (flags == strictConversion) {
438                    source -= (extraBytesToRead+1); /* return to the illegal value itself */
439                    result = sourceIllegal;
440                    break;
441                } else {
442                    *target++ = UNI_REPLACEMENT_CHAR;
443                }
444            } else {
445                *target++ = (UTF16)ch; /* normal case */
446            }
447        } else if (ch > UNI_MAX_UTF16) {
448            if (flags == strictConversion) {
449                result = sourceIllegal;
450                source -= (extraBytesToRead+1); /* return to the start */
451                break; /* Bail out; shouldn't continue */
452            } else {
453                *target++ = UNI_REPLACEMENT_CHAR;
454            }
455        } else {
456            /* target is a character in range 0xFFFF - 0x10FFFF. */
457            if (target + 1 >= targetEnd) {
458                source -= (extraBytesToRead+1); /* Back up source pointer! */
459                result = targetExhausted; break;
460            }
461            ch -= halfBase;
462            *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
463            *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
464        }
465    }
466    *sourceStart = source;
467    *targetStart = target;
468    return result;
469}
470
471/* --------------------------------------------------------------------- */
472
473ConversionResult ConvertUTF32toUTF8 (
474        const UTF32** sourceStart, const UTF32* sourceEnd, 
475        UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
476    ConversionResult result = conversionOK;
477    const UTF32* source = *sourceStart;
478    UTF8* target = *targetStart;
479    while (source < sourceEnd) {
480        UTF32 ch;
481        unsigned short bytesToWrite = 0;
482        const UTF32 byteMask = 0xBF;
483        const UTF32 byteMark = 0x80; 
484        ch = *source++;
485        if (flags == strictConversion ) {
486            /* UTF-16 surrogate values are illegal in UTF-32 */
487            if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
488                --source; /* return to the illegal value itself */
489                result = sourceIllegal;
490                break;
491            }
492        }
493        /*
494         * Figure out how many bytes the result will require. Turn any
495         * illegally large UTF32 things (> Plane 17) into replacement chars.
496         */
497        if (ch < (UTF32)0x80) {      bytesToWrite = 1;
498        } else if (ch < (UTF32)0x800) {     bytesToWrite = 2;
499        } else if (ch < (UTF32)0x10000) {   bytesToWrite = 3;
500        } else if (ch <= UNI_MAX_LEGAL_UTF32) {  bytesToWrite = 4;
501        } else {                            bytesToWrite = 3;
502                                            ch = UNI_REPLACEMENT_CHAR;
503                                            result = sourceIllegal;
504        }
505       
506        target += bytesToWrite;
507        if (target > targetEnd) {
508            --source; /* Back up source pointer! */
509            target -= bytesToWrite; result = targetExhausted; break;
510        }
511        switch (bytesToWrite) { /* note: everything falls through. */
512            case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
513            case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
514            case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
515            case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]);
516        }
517        target += bytesToWrite;
518    }
519    *sourceStart = source;
520    *targetStart = target;
521    return result;
522}
523
524/* --------------------------------------------------------------------- */
525
526ConversionResult ConvertUTF8toUTF32 (
527        const UTF8** sourceStart, const UTF8* sourceEnd, 
528        UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) {
529    ConversionResult result = conversionOK;
530    const UTF8* source = *sourceStart;
531    UTF32* target = *targetStart;
532    while (source < sourceEnd) {
533        UTF32 ch = 0;
534        unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
535        if (source + extraBytesToRead >= sourceEnd) {
536            result = sourceExhausted; break;
537        }
538        /* Do this check whether lenient or strict */
539        if (! isLegalUTF8(source, extraBytesToRead+1)) {
540            result = sourceIllegal;
541            break;
542        }
543        /*
544         * The cases all fall through. See "Note A" below.
545         */
546        switch (extraBytesToRead) {
547            case 5: ch += *source++; ch <<= 6;
548            case 4: ch += *source++; ch <<= 6;
549            case 3: ch += *source++; ch <<= 6;
550            case 2: ch += *source++; ch <<= 6;
551            case 1: ch += *source++; ch <<= 6;
552            case 0: ch += *source++;
553        }
554        ch -= offsetsFromUTF8[extraBytesToRead];
555
556        if (target >= targetEnd) {
557            source -= (extraBytesToRead+1); /* Back up the source pointer! */
558            result = targetExhausted; break;
559        }
560        if (ch <= UNI_MAX_LEGAL_UTF32) {
561            /*
562             * UTF-16 surrogate values are illegal in UTF-32, and anything
563             * over Plane 17 (> 0x10FFFF) is illegal.
564             */
565            if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
566                if (flags == strictConversion) {
567                    source -= (extraBytesToRead+1); /* return to the illegal value itself */
568                    result = sourceIllegal;
569                    break;
570                } else {
571                    *target++ = UNI_REPLACEMENT_CHAR;
572                }
573            } else {
574                *target++ = ch;
575            }
576        } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */
577            result = sourceIllegal;
578            *target++ = UNI_REPLACEMENT_CHAR;
579        }
580    }
581    *sourceStart = source;
582    *targetStart = target;
583    return result;
584}
585
586/* ---------------------------------------------------------------------
587
588    Note A.
589    The fall-through switches in UTF-8 reading code save a
590    temp variable, some decrements & conditionals.  The switches
591    are equivalent to the following loop:
592        {
593            int tmpBytesToRead = extraBytesToRead+1;
594            do {
595                ch += *source++;
596                --tmpBytesToRead;
597                if (tmpBytesToRead) ch <<= 6;
598            } while (tmpBytesToRead > 0);
599        }
600    In UTF-8 writing code, the switches on "bytesToWrite" are
601    similarly unrolled loops.
602
603   --------------------------------------------------------------------- */
Note: See TracBrowser for help on using the repository browser.