Compare commits

...
12 Commits
Author SHA1 Message Date
dgp abacbb320b Remove last knownBug. 2020-04-20 05:22:27 +00:00
dgp fac62d70e3 Apply fix for [2738427] and adjust tests. 2020-04-20 05:17:24 +00:00
dgp f6278d14a3 Apply fix for [493dccc2de] and adjust tests. 2020-04-20 05:09:05 +00:00
dgp f4b66be2ec Apply fix for [5e6346a252] and adjust tests. 2020-04-20 04:58:52 +00:00
dgp 8f1c3cd0e5 Apply first fix of [c61818e4c9] and adjust tests. 2020-04-20 04:40:38 +00:00
dgp fb201ad904 Remove one misplaced "compat85" constraint.
Note that "compat85" is being used to conflate two separable issues.
1) Whether or not 4-byte sequences are encoded / decoded in UTF-8.
2) Whether surrogate pairs are encoded / decoded in UTF-16.
These matters should be represented with separate constraints.
2020-04-20 04:19:06 +00:00
dgp 8c522d9c41 Apply the overlong rejection backport fix again, but this time don't
attempt to include surrogate support. This was the first attempt on 
the 8.5 branch to leave behind the long outdated FSS-UTF approach that
suggested support for 5- and 6-byte sequence support.  Clearly there was
no useful support for a TCL_UTF_MAX=4 build before this, and a 
TCL_UTF_MAX=6 build could have only been nonsense. There's no history
worth reclaiming compatibility with in the custom builds.
2020-04-20 04:02:51 +00:00
dgp b76a882c7a We know that Tcl 8.5.19 suffered from [2738427] where Tcl_NumUtfChars did
not handle incomplete sequences at the end of string.
2020-04-19 23:23:53 +00:00
dgp 116a9a7f78 We know that Tcl 8.5.19 does not support Unicode 11. 2020-04-19 23:12:52 +00:00
dgp b798e429f8 We know the Tcl 8.5.19 suffers from [c61818e4c9] where Tcl_UtfPrev
uncritically accepts all bytes >= 0xC0 as valid lead bytes and makes no check
in the number of trail bytes.
2020-04-19 23:12:02 +00:00
dgp 60fc9fe4c5 We know that Tcl 8.5.19 suffers from [67aa9a2070] -- accepting overlong. 2020-04-19 23:00:22 +00:00
dgp 1770558983 Attach modern testing machinery and tests to a pre-backport state of Tcl 8.5
to see what compatibility with released Tcl 8.5 empirically means.
NOTE: this returns to a state of supporting long-outdated FSS-UTF.
2020-04-19 22:47:17 +00:00
3 changed files with 1543 additions and 629 deletions
+606 -474
View File
File diff suppressed because it is too large Load Diff
+206 -42
View File
@@ -66,7 +66,7 @@ static CONST unsigned char totalBytes[256] = {
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,
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,
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,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
#if TCL_UTF_MAX > 3
4,4,4,4,4,4,4,4,
@@ -81,6 +81,7 @@ static CONST unsigned char totalBytes[256] = {
*/
static int UtfCount(int ch);
static int Invalid(unsigned char *src);
/*
*---------------------------------------------------------------------------
@@ -115,7 +116,61 @@ UtfCount(
#endif
return 3;
}
/*
*---------------------------------------------------------------------------
*
* Invalid --
*
* Utility routine to report whether /src/ points to the start of an
* invald byte sequence that should be rejected. This might be because
* it is an overlong encoding, or because it encodes something out of
* the proper range. Caller guarantees that src[0] and src[1] are
* readable, and
*
* (src[0] >= 0xC0) && (src[0] != 0xC1)
* (src[1] >= 0x80) && (src[1] < 0xC0)
* (src[0] < ((TCL_UTF_MAX > 3) ? 0xF5 : 0xF0))
*
* Results:
* A boolean.
*---------------------------------------------------------------------------
*/
static CONST unsigned char bounds[28] = {
0x80, 0x80, /* \xC0 accepts \x80 only */
0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF,
0x80, 0xBF, /* (\xC4 - \xDC) -- all sequences valid */
0xA0, 0xBF, /* \xE0\x80 through \xE0\x9F are invalid prefixes */
0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF, /* (\xE4 - \xEC) -- all valid */
#if TCL_UTF_MAX > 3
0x90, 0xBF, /* \xF0\x80 through \xF0\x8F are invalid prefixes */
0x80, 0x8F /* \xF4\x90 and higher are invalid prefixes */
#else
0xC0, 0xBF, /* Not used, but reject all again for safety. */
0xC0, 0xBF /* Not used, but reject all again for safety. */
#endif
};
INLINE static int
Invalid(
unsigned char *src) /* Points to lead byte of a UTF-8 byte sequence */
{
unsigned char byte = *src;
int index;
if (byte % 0x04) {
/* Only lead bytes 0xC0, 0xE0, 0xF0, 0xF4 need examination */
return 0;
}
index = (byte - 0xC0) >> 1;
if (src[1] < bounds[index] || src[1] > bounds[index+1]) {
/* Out of bounds - report invalid. */
return 1;
}
return 0;
}
/*
*---------------------------------------------------------------------------
*
@@ -154,7 +209,7 @@ Tcl_UniCharToUtf(
return 2;
}
if (ch <= 0xFFFF) {
#if TCL_UTF_MAX == 4
#if 0 && TCL_UTF_MAX == 4
if ((ch & 0xF800) == 0xD800) {
if (ch & 0x0400) {
/* Low surrogate */
@@ -464,7 +519,6 @@ Tcl_NumUtfChars(
* for strlen(string). */
{
Tcl_UniChar ch;
register Tcl_UniChar *chPtr = &ch;
register int i;
/*
@@ -477,23 +531,25 @@ Tcl_NumUtfChars(
i = 0;
if (length < 0) {
while (*src != '\0') {
src += TclUtfToUniChar(src, chPtr);
src += TclUtfToUniChar(src, &ch);
i++;
}
if (i < 0) i = INT_MAX; /* Bug [2738427] */
} else {
register int n;
register const char *endPtr = src + length - TCL_UTF_MAX;
while (length > 0) {
if (UCHAR(*src) < 0xC0) {
length--;
src++;
} else {
n = Tcl_UtfToUniChar(src, chPtr);
length -= n;
src += n;
}
while (src < endPtr) {
src += TclUtfToUniChar(src, &ch);
i++;
}
endPtr += TCL_UTF_MAX;
while ((src < endPtr) && Tcl_UtfCharComplete(src, endPtr - src)) {
src += TclUtfToUniChar(src, &ch);
i++;
}
if (src < endPtr) {
i += endPtr - src;
}
}
return i;
}
@@ -602,9 +658,26 @@ CONST char *
Tcl_UtfNext(
CONST char *src) /* The current location in the string. */
{
Tcl_UniChar ch;
int byte = *((unsigned char *) src);
int left = totalBytes[byte];
const char *next = src + 1;
return src + TclUtfToUniChar(src, &ch);
while (--left) {
byte = *((unsigned char *) next);
if ((byte & 0xC0) != 0x80) {
/*
* src points to non-trail byte; We ran out of trail bytes
* before the needs of the lead byte were satisfied.
* Let the (malformed) lead byte alone be a character
*/
return src + 1;
}
next++;
}
if (Invalid((unsigned char *)src)) {
return src + 1;
}
return next;
}
/*
@@ -612,15 +685,43 @@ Tcl_UtfNext(
*
* Tcl_UtfPrev --
*
* Given a pointer to some current location in a UTF-8 string, move
* backwards one character. This works correctly when the pointer is in
* the middle of a UTF-8 character.
* The aim of this routine is to provide a way to move backward
* through a UTF-8 string. The caller is expected to pass non-NULL
* pointer arguments start and src. start points to the beginning
* of a string, and src >= start points to a location within (or just
* past the end) of the string. This routine always returns a
* pointer within the string (>= start). When (src == start), it
* returns start. When (src > start), it returns a pointer (< src)
* and (>= src - TCL_UTF_MAX). Subject to these constraints, the
* routine returns a pointer to the earliest byte in the string that
* starts a character when characters are read starting at start and
* that character might include the byte src[-1]. The routine will
* examine only those bytes in the range that might be returned.
* It will not examine the byte *src, and because of that cannot
* determine for certain in all circumstances whether the character
* that begins with the returned pointer will or will not include
* the byte src[-1]. In the scenario, where src points to the end of
* a buffer being filled, the returned pointer point to either the
* final complete character in the string or to the earliest byte
* that might start an incomplete character waiting for more bytes to
* complete.
*
* Because this routine always returns a value < src until the point
* it is forced to return start, it is useful as a backward iterator
* through a string that will always make progress and always be
* prevented from running past the beginning of the string.
*
* In a string where all characters are complete and properly formed,
* and the value of src points to the first byte of a character,
* repeated Tcl_UtfPrev calls will step to the starting bytes of
* characters, one character at a time. Within those limitations,
* Tcl_UtfPrev and Tcl_UtfNext are inverses. If either condition cannot
* be met, Tcl_UtfPrev and Tcl_UtfNext may not function as inverses and
* the caller will have to take greater care.
*
* Results:
* The return value is a pointer to the previous character in the UTF-8
* string. If the current location was already at the beginning of the
* string, the return value will also be a pointer to the beginning of
* the string.
* A pointer to the start of a character in the string as described
* above.
*
* Side effects:
* None.
@@ -630,32 +731,95 @@ Tcl_UtfNext(
CONST char *
Tcl_UtfPrev(
CONST char *src, /* The current location in the string. */
CONST char *start) /* Pointer to the beginning of the string, to
* avoid going backwards too far. */
CONST char *src, /* A location in a UTF-8 string. */
CONST char *start) /* Pointer to the beginning of the string */
{
CONST char *look;
int i, byte;
int trailBytesSeen = 0; /* How many trail bytes have been verified? */
CONST char *fallback = src - 1;
/* If we cannot find a lead byte that might
* start a prefix of a valid UTF byte sequence,
* we will fallback to a one-byte back step */
unsigned char *look = (unsigned char *)fallback;
/* Start search at the fallback position */
/* Quick boundary case exit. */
if (fallback <= start) {
return start;
}
do {
unsigned char byte = look[0];
src--;
look = src;
for (i = 0; i < TCL_UTF_MAX; i++) {
if (look < start) {
if (src < start) {
src = start;
}
break;
}
byte = *((unsigned char *) look);
if (byte < 0x80) {
break;
/*
* Single byte character. Either this is a correct previous
* character, or it is followed by at least one trail byte
* which indicates a malformed sequence. In either case the
* correct result is to return the fallback.
*/
return fallback;
}
if (byte >= 0xC0) {
return look;
/* Non-trail byte; May be multibyte lead. */
if ((trailBytesSeen == 0)
/*
* We've seen no trailing context to use to check
* anything. From what we know, this non-trail byte
* is a prefix of a previous character, and accepting
* it (the fallback) is correct.
*/
|| (trailBytesSeen >= totalBytes[byte])) {
/*
* That is, (1 + trailBytesSeen > needed).
* We've examined more bytes than needed to complete
* this lead byte. No matter about well-formedness or
* validity, the sequence starting with this lead byte
* will never include the fallback location, so we must
* return the fallback location. See test utf-7.17
*/
return fallback;
}
/*
* trailBytesSeen > 0, so we can examine look[1] safely.
* Use that capability to screen out invalid sequences.
*/
if (Invalid(look)) {
/* Reject */
return fallback;
}
return (CONST char *)look;
}
/* We saw a trail byte. */
trailBytesSeen++;
if ((CONST char *)look == start) {
/*
* Do not read before the start of the string
*
* If we get here, we've examined bytes at every location
* >= start and < src and all of them are trail bytes,
* including (*start). We need to return our fallback
* and exit this loop before we run past the start of the string.
*/
return fallback;
}
/* Continue the search backwards... */
look--;
}
return src;
} while (trailBytesSeen < TCL_UTF_MAX);
/*
* We've seen TCL_UTF_MAX trail bytes, so we know there will not be a
* properly formed byte sequence to find, and we can stop looking,
* accepting the fallback.
*/
return fallback;
}
/*
+731 -113
View File
File diff suppressed because it is too large Load Diff