#include "tidy.h"
#include "forward.h"
#include "utf8.h"
#define kNumUTF8Sequences 7
#define kMaxUTF8Bytes 4
#define kUTF8ByteSwapNotAChar 0xFFFE
#define kUTF8NotAChar 0xFFFF
#define kMaxUTF8FromUCS4 0x10FFFF
#define kUTF16SurrogatesBegin 0x10000
#define kMaxUTF16FromUCS4 0x10FFFF
#define kUTF16LowSurrogateBegin 0xD800
#define kUTF16LowSurrogateEnd 0xDBFF
#define kUTF16HighSurrogateBegin 0xDC00
#define kUTF16HighSurrogateEnd 0xDFFF
static const int offsetUTF8Sequences[kMaxUTF8Bytes + 1] =
{
0,
1,
2,
4,
kNumUTF8Sequences
};
static const struct validUTF8Sequence
{
uint lowChar;
uint highChar;
int numBytes;
byte validBytes[8];
} validUTF8[kNumUTF8Sequences] =
{
{0x0000, 0x007F, 1, {0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{0x0080, 0x07FF, 2, {0xC2, 0xDF, 0x80, 0xBF, 0x00, 0x00, 0x00, 0x00}},
{0x0800, 0x0FFF, 3, {0xE0, 0xE0, 0xA0, 0xBF, 0x80, 0xBF, 0x00, 0x00}},
{0x1000, 0xFFFF, 3, {0xE1, 0xEF, 0x80, 0xBF, 0x80, 0xBF, 0x00, 0x00}},
{0x10000, 0x3FFFF, 4, {0xF0, 0xF0, 0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}},
{0x40000, 0xFFFFF, 4, {0xF1, 0xF3, 0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}},
{0x100000, 0x10FFFF, 4, {0xF4, 0xF4, 0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}}
};
int TY_(DecodeUTF8BytesToChar)( uint* c, uint firstByte, ctmbstr successorBytes,
TidyInputSource* inp, int* count )
{
byte tempbuf[10];
byte *buf = &tempbuf[0];
uint ch = 0, n = 0;
int i, bytes = 0;
Bool hasError = no;
if ( successorBytes )
buf = (byte*) successorBytes;
if ( firstByte == EndOfStream )
{
*c = firstByte;
*count = 1;
return 0;
}
ch = firstByte;
if (ch <= 0x7F)
{
n = ch;
bytes = 1;
}
else if ((ch & 0xE0) == 0xC0)
{
n = ch & 31;
bytes = 2;
}
else if ((ch & 0xF0) == 0xE0)
{
n = ch & 15;
bytes = 3;
}
else if ((ch & 0xF8) == 0xF0)
{
n = ch & 7;
bytes = 4;
}
else if ((ch & 0xFC) == 0xF8)
{
n = ch & 3;
bytes = 5;
hasError = yes;
}
else if ((ch & 0xFE) == 0xFC)
{
n = ch & 1;
bytes = 6;
hasError = yes;
}
else
{
n = ch;
bytes = 1;
hasError = yes;
}
if ( successorBytes )
{
for ( i=0; i < bytes-1; ++i )
{
if ( !buf[i] || (buf[i] & 0xC0) != 0x80 )
{
hasError = yes;
bytes = i;
break;
}
n = (n << 6) | (buf[i] & 0x3F);
}
}
else if ( inp )
{
for ( i=0; i < bytes-1 && !inp->eof(inp->sourceData); ++i )
{
int b = inp->getByte( inp->sourceData );
buf[i] = (tmbchar) b;
if ( b == EOF || (buf[i] & 0xC0) != 0x80 )
{
hasError = yes;
bytes = i;
if ( b != EOF )
inp->ungetByte( inp->sourceData, buf[i] );
break;
}
n = (n << 6) | (buf[i] & 0x3F);
}
}
else if ( bytes > 1 )
{
hasError = yes;
bytes = 1;
}
if (!hasError && ((n == kUTF8ByteSwapNotAChar) || (n == kUTF8NotAChar)))
hasError = yes;
if (!hasError && (n > kMaxUTF8FromUCS4))
hasError = yes;
#if 0
if (!hasError && (n >= kUTF16LowSurrogateBegin) && (n <= kUTF16HighSurrogateEnd))
hasError = yes;
#endif
if (!hasError)
{
int lo, hi;
lo = offsetUTF8Sequences[bytes - 1];
hi = offsetUTF8Sequences[bytes] - 1;
if ((n < validUTF8[lo].lowChar) || (n > validUTF8[hi].highChar))
hasError = yes;
else
{
hasError = yes;
for (i = lo; i <= hi; i++)
{
int tempCount;
byte theByte;
for (tempCount = 0; tempCount < bytes; tempCount++)
{
if (!tempCount)
theByte = (tmbchar) firstByte;
else
theByte = buf[tempCount - 1];
if ( theByte >= validUTF8[i].validBytes[(tempCount * 2)] &&
theByte <= validUTF8[i].validBytes[(tempCount * 2) + 1] )
hasError = no;
if (hasError)
break;
}
}
}
}
#if 1 && defined(_DEBUG)
if ( hasError )
{
fprintf( stderr, "UTF-8 decoding error of %d bytes : ", bytes );
fprintf( stderr, "0x%02x ", firstByte );
for (i = 1; i < bytes; i++)
fprintf( stderr, "0x%02x ", buf[i - 1] );
fprintf( stderr, " = U+%04ulx\n", n );
}
#endif
*count = bytes;
*c = n;
if ( hasError )
return -1;
return 0;
}
int TY_(EncodeCharToUTF8Bytes)( uint c, tmbstr encodebuf,
TidyOutputSink* outp, int* count )
{
byte tempbuf[10] = {0};
byte* buf = &tempbuf[0];
int bytes = 0;
Bool hasError = no;
if ( encodebuf )
buf = (byte*) encodebuf;
if (c <= 0x7F)
{
buf[0] = (tmbchar) c;
bytes = 1;
}
else if (c <= 0x7FF)
{
buf[0] = (tmbchar) ( 0xC0 | (c >> 6) );
buf[1] = (tmbchar) ( 0x80 | (c & 0x3F) );
bytes = 2;
}
else if (c <= 0xFFFF)
{
buf[0] = (tmbchar) (0xE0 | (c >> 12));
buf[1] = (tmbchar) (0x80 | ((c >> 6) & 0x3F));
buf[2] = (tmbchar) (0x80 | (c & 0x3F));
bytes = 3;
if ( c == kUTF8ByteSwapNotAChar || c == kUTF8NotAChar )
hasError = yes;
#if 0
else if ( c >= kUTF16LowSurrogateBegin && c <= kUTF16HighSurrogateEnd )
hasError = yes;
#endif
}
else if (c <= 0x1FFFFF)
{
buf[0] = (tmbchar) (0xF0 | (c >> 18));
buf[1] = (tmbchar) (0x80 | ((c >> 12) & 0x3F));
buf[2] = (tmbchar) (0x80 | ((c >> 6) & 0x3F));
buf[3] = (tmbchar) (0x80 | (c & 0x3F));
bytes = 4;
if (c > kMaxUTF8FromUCS4)
hasError = yes;
}
else if (c <= 0x3FFFFFF)
{
buf[0] = (tmbchar) (0xF8 | (c >> 24));
buf[1] = (tmbchar) (0x80 | (c >> 18));
buf[2] = (tmbchar) (0x80 | ((c >> 12) & 0x3F));
buf[3] = (tmbchar) (0x80 | ((c >> 6) & 0x3F));
buf[4] = (tmbchar) (0x80 | (c & 0x3F));
bytes = 5;
hasError = yes;
}
else if (c <= 0x7FFFFFFF)
{
buf[0] = (tmbchar) (0xFC | (c >> 30));
buf[1] = (tmbchar) (0x80 | ((c >> 24) & 0x3F));
buf[2] = (tmbchar) (0x80 | ((c >> 18) & 0x3F));
buf[3] = (tmbchar) (0x80 | ((c >> 12) & 0x3F));
buf[4] = (tmbchar) (0x80 | ((c >> 6) & 0x3F));
buf[5] = (tmbchar) (0x80 | (c & 0x3F));
bytes = 6;
hasError = yes;
}
else
hasError = yes;
if ( !hasError && outp != NULL )
{
int ix;
for ( ix=0; ix < bytes; ++ix )
outp->putByte( outp->sinkData, buf[ix] );
}
#if 1 && defined(_DEBUG)
if ( hasError )
{
int i;
fprintf( stderr, "UTF-8 encoding error for U+%x : ", c );
for (i = 0; i < bytes; i++)
fprintf( stderr, "0x%02x ", buf[i] );
fprintf( stderr, "\n" );
}
#endif
*count = bytes;
if (hasError)
return -1;
return 0;
}
uint TY_(GetUTF8)( ctmbstr str, uint *ch )
{
uint n;
int bytes;
int err;
bytes = 0;
err = TY_(DecodeUTF8BytesToChar)( &n, str[0], str+1, NULL, &bytes );
if (err)
{
#if 1 && defined(_DEBUG)
fprintf(stderr, "pprint UTF-8 decoding error for U+%x : ", n);
#endif
n = 0xFFFD;
}
*ch = n;
return bytes - 1;
}
tmbstr TY_(PutUTF8)( tmbstr buf, uint c )
{
int err, count = 0;
err = TY_(EncodeCharToUTF8Bytes)( c, buf, NULL, &count );
if (err)
{
#if 1 && defined(_DEBUG)
fprintf(stderr, "pprint UTF-8 encoding error for U+%x : ", c);
#endif
buf[0] = (byte) 0xEF;
buf[1] = (byte) 0xBF;
buf[2] = (byte) 0xBD;
count = 3;
}
buf += count;
return buf;
}
Bool TY_(IsValidUTF16FromUCS4)( tchar ucs4 )
{
return ( ucs4 <= kMaxUTF16FromUCS4 );
}
Bool TY_(IsHighSurrogate)( tchar ch )
{
return ( ch >= kUTF16HighSurrogateBegin && ch <= kUTF16HighSurrogateEnd );
}
Bool TY_(IsLowSurrogate)( tchar ch )
{
return ( ch >= kUTF16LowSurrogateBegin && ch <= kUTF16LowSurrogateEnd );
}
tchar TY_(CombineSurrogatePair)( tchar high, tchar low )
{
assert( TY_(IsHighSurrogate)(high) && TY_(IsLowSurrogate)(low) );
return ( ((low - kUTF16LowSurrogateBegin) * 0x400) +
high - kUTF16HighSurrogateBegin + 0x10000 );
}
Bool TY_(SplitSurrogatePair)( tchar utf16, tchar* low, tchar* high )
{
Bool status = ( TY_(IsValidCombinedChar)( utf16 ) && high && low );
if ( status )
{
*low = (utf16 - kUTF16SurrogatesBegin) / 0x400 + kUTF16LowSurrogateBegin;
*high = (utf16 - kUTF16SurrogatesBegin) % 0x400 + kUTF16HighSurrogateBegin;
}
return status;
}
Bool TY_(IsValidCombinedChar)( tchar ch )
{
return ( ch >= kUTF16SurrogatesBegin &&
(ch & 0x0000FFFE) != 0x0000FFFE &&
(ch & 0x0000FFFF) != 0x0000FFFF );
}
Bool TY_(IsCombinedChar)( tchar ch )
{
return ( ch >= kUTF16SurrogatesBegin );
}