X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=utf8.c;h=9ac63a6ffd620ac822f63e0d4ccafadbf465467d;hb=a6dd486b7feb5918da837e5ad585c8ce954f9bbf;hp=98236ed170903bb889c21aa8c3bd44dc9ecfa478;hpb=ba210ebec161cde003bc967e8e460c72f71fb70c;p=p5sagit%2Fp5-mst-13.2.git diff --git a/utf8.c b/utf8.c index 98236ed..9ac63a6 100644 --- a/utf8.c +++ b/utf8.c @@ -104,27 +104,41 @@ Perl_uv_to_utf8(pTHX_ U8 *d, UV uv) /* Tests if some arbitrary number of bytes begins in a valid UTF-8 character. * The actual number of bytes in the UTF-8 character will be returned if it * is valid, otherwise 0. */ -int +STRLEN Perl_is_utf8_char(pTHX_ U8 *s) { U8 u = *s; - int slen, len; + STRLEN slen, len; + UV uv, ouv; - if (!(u & 0x80)) + if (u <= 0x7f) return 1; - if (!(u & 0x40)) + if (u >= 0x80 && u <= 0xbf) return 0; len = UTF8SKIP(s); + if (len < 2 || (u >= 0xc0 && u <= 0xfd && s[1] < 0x80)) + return 0; + slen = len - 1; s++; + uv = u; + ouv = uv; while (slen--) { if ((*s & 0xc0) != 0x80) return 0; + uv = (uv << 6) | (*s & 0x3f); + if (uv < ouv) + return 0; + ouv = uv; s++; } + + if (UNISKIP(uv) < len) + return 0; + return len; } @@ -140,20 +154,24 @@ string, false otherwise. bool Perl_is_utf8_string(pTHX_ U8 *s, STRLEN len) { - U8* x=s; - U8* send=s+len; - int c; + U8* x = s; + U8* send = s + len; + STRLEN c; + while (x < send) { c = is_utf8_char(x); + if (!c) + return FALSE; x += c; - if (!c || x > send) - return 0; + if (x > send) + return FALSE; } - return 1; + + return TRUE; } /* -=for apidoc Am|U8* s|utf8_to_uv_chk|STRLEN curlen|I32 *retlen|I32 checking +=for apidoc Am|U8* s|utf8_to_uv|STRLEN curlen|I32 *retlen|U32 flags Returns the character value of the first character in the string C which is assumed to be in UTF8 encoding and no longer than C; @@ -161,16 +179,15 @@ C will be set to the length, in bytes, of that character, and the pointer C will be advanced to the end of the character. If C does not point to a well-formed UTF8 character, the behaviour -is dependent on the value of C: if this is true, it is -assumed that the caller will raise a warning, and this function will -set C to C<-1> and return. If C is not true, an optional UTF8 -warning is produced. +is dependent on the value of C: if it contains UTF8_CHECK_ONLY, +it is assumed that the caller will raise a warning, and this function +will set C to C<-1> and return. The C can also contain +various flags to allow deviations from the strict UTF-8 encoding. -=cut -*/ +=cut */ UV -Perl_utf8_to_uv_chk(pTHX_ U8* s, STRLEN curlen, STRLEN* retlen, bool checking) +Perl_utf8_to_uv(pTHX_ U8* s, STRLEN curlen, STRLEN* retlen, U32 flags) { dTHR; UV uv = *s, ouv; @@ -184,7 +201,8 @@ Perl_utf8_to_uv_chk(pTHX_ U8* s, STRLEN curlen, STRLEN* retlen, bool checking) return *s; } - if (uv >= 0x80 && uv <= 0xbf) { + if ((uv >= 0x80 && uv <= 0xbf) && + !(flags & UTF8_ALLOW_CONTINUATION)) { if (dowarn) Perl_warner(aTHX_ WARN_UTF8, "Malformed UTF-8 character (unexpected continuation byte 0x%02x)", @@ -192,22 +210,24 @@ Perl_utf8_to_uv_chk(pTHX_ U8* s, STRLEN curlen, STRLEN* retlen, bool checking) goto malformed; } - if (uv >= 0xc0 && uv <= 0xfd && curlen > 1 && s[1] < 0x80) { + if ((uv >= 0xc0 && uv <= 0xfd && curlen >1 && s[1] < 0x80) && + !(flags & UTF8_ALLOW_NON_CONTINUATION)) { if (dowarn) Perl_warner(aTHX_ WARN_UTF8, "Malformed UTF-8 character (unexpected non-continuation byte 0x%02x after byte 0x%02x)", s[1], uv); goto malformed; } - - if ((uv == 0xfe || uv == 0xff) && IN_UTF8){ + + if ((uv == 0xfe || uv == 0xff) && + !(flags & UTF8_ALLOW_FE_FF)) { if (dowarn) Perl_warner(aTHX_ WARN_UTF8, - "Malformed UTF-8 character (impossible byte 0x%02x)", + "Malformed UTF-8 character (byte 0x%02x)", uv); goto malformed; } - + if (!(uv & 0x20)) { len = 2; uv &= 0x1f; } else if (!(uv & 0x10)) { len = 3; uv &= 0x0f; } else if (!(uv & 0x08)) { len = 4; uv &= 0x07; } @@ -215,13 +235,14 @@ Perl_utf8_to_uv_chk(pTHX_ U8* s, STRLEN curlen, STRLEN* retlen, bool checking) else if (!(uv & 0x02)) { len = 6; uv &= 0x01; } else if (!(uv & 0x01)) { len = 7; uv = 0; } else { len = 13; uv = 0; } /* whoa! */ - + if (retlen) *retlen = len; expectlen = len; - if (curlen < expectlen) { + if ((curlen < expectlen) && + !(flags & UTF8_ALLOW_SHORT)) { if (dowarn) Perl_warner(aTHX_ WARN_UTF8, "Malformed UTF-8 character (%d byte%s, need %d)", @@ -244,6 +265,7 @@ Perl_utf8_to_uv_chk(pTHX_ U8* s, STRLEN curlen, STRLEN* retlen, bool checking) else uv = (uv << 6) | (*s & 0x3f); if (uv < ouv) { + /* This cannot be allowed. */ if (dowarn) Perl_warner(aTHX_ WARN_UTF8, "Malformed UTF-8 character (overflow at 0x%"UVxf", byte 0x%02x)", @@ -254,29 +276,33 @@ Perl_utf8_to_uv_chk(pTHX_ U8* s, STRLEN curlen, STRLEN* retlen, bool checking) ouv = uv; } - if (uv >= 0xd800 && uv <= 0xdfff) { + if ((uv >= 0xd800 && uv <= 0xdfff) && + !(flags & UTF8_ALLOW_SURROGATE)) { if (dowarn) Perl_warner(aTHX_ WARN_UTF8, "Malformed UTF-8 character (UTF-16 surrogate 0x%04"UVxf")", uv); goto malformed; - } else if (uv == 0xfffe) { + } else if ((uv == 0xfffe) && + !(flags & UTF8_ALLOW_BOM)) { if (dowarn) Perl_warner(aTHX_ WARN_UTF8, "Malformed UTF-8 character (byte order mark 0x%04"UVxf")", uv); goto malformed; - } else if (uv == 0xffff && IN_UTF8) { + } else if ((uv == 0xffff) && + !(flags & UTF8_ALLOW_FFFF)) { if (dowarn) Perl_warner(aTHX_ WARN_UTF8, - "Malformed UTF-8 character (impossible character 0x%04"UVxf")", + "Malformed UTF-8 character (character 0x%04"UVxf")", uv); goto malformed; - } else if (expectlen > UTF8LEN(uv)) { + } else if ((expectlen > UNISKIP(uv)) && + !(flags & UTF8_ALLOW_LONG)) { if (dowarn) Perl_warner(aTHX_ WARN_UTF8, "Malformed UTF-8 character (%d byte%s, need %d)", - expectlen, expectlen > 1 ? "s": "", UTF8LEN(uv)); + expectlen, expectlen > 1 ? "s": "", UNISKIP(uv)); goto malformed; } @@ -284,7 +310,7 @@ Perl_utf8_to_uv_chk(pTHX_ U8* s, STRLEN curlen, STRLEN* retlen, bool checking) malformed: - if (checking) { + if (flags & UTF8_CHECK_ONLY) { if (retlen) *retlen = len; return 0; @@ -297,7 +323,7 @@ malformed: } /* -=for apidoc Am|U8* s|utf8_to_uv|STRLEN *retlen +=for apidoc Am|U8* s|utf8_to_uv_simple|STRLEN *retlen Returns the character value of the first character in the string C which is assumed to be in UTF8 encoding; C will be set to the @@ -311,9 +337,9 @@ returned and retlen is set, if possible, to -1. */ UV -Perl_utf8_to_uv(pTHX_ U8* s, STRLEN* retlen) +Perl_utf8_to_uv_simple(pTHX_ U8* s, STRLEN* retlen) { - return Perl_utf8_to_uv_chk(aTHX_ s, (STRLEN)-1, retlen, 0); + return Perl_utf8_to_uv(aTHX_ s, (STRLEN)-1, retlen, 0); } /* utf8_distance(a,b) returns the number of UTF8 characters between @@ -373,30 +399,30 @@ Returns zero on failure, setting C to -1. U8 * Perl_utf8_to_bytes(pTHX_ U8* s, STRLEN *len) { - dTHR; U8 *send; U8 *d; - U8 *save; - - send = s + *len; - d = save = s; + U8 *save = s; /* ensure valid UTF8 and chars < 256 before updating string */ - while (s < send) { - U8 c = *s++; + for (send = s + *len; s < send; ) { + U8 c = *s++; + if (c >= 0x80 && - ( (s >= send) || ((*s++ & 0xc0) != 0x80) || ((c & 0xfe) != 0xc2))) { - *len = -1; - return 0; - } + ((s >= send) || + ((*s++ & 0xc0) != 0x80) || ((c & 0xfe) != 0xc2))) { + *len = -1; + return 0; + } } - s = save; + + d = s = save; while (s < send) { - if (*s < 0x80) - *d++ = *s++; + if (*s < 0x80) { + *d++ = *s++; + } else { STRLEN ulen; - *d++ = (U8)utf8_to_uv(s, &ulen); + *d++ = (U8)utf8_to_uv_simple(s, &ulen); s += ulen; } } @@ -924,7 +950,7 @@ Perl_to_utf8_upper(pTHX_ U8 *p) if (!PL_utf8_toupper) PL_utf8_toupper = swash_init("utf8", "ToUpper", &PL_sv_undef, 4, 0); uv = swash_fetch(PL_utf8_toupper, p); - return uv ? uv : utf8_to_uv_chk(p,STRLEN_MAX,0,0); + return uv ? uv : utf8_to_uv(p,STRLEN_MAX,0,0); } UV @@ -935,7 +961,7 @@ Perl_to_utf8_title(pTHX_ U8 *p) if (!PL_utf8_totitle) PL_utf8_totitle = swash_init("utf8", "ToTitle", &PL_sv_undef, 4, 0); uv = swash_fetch(PL_utf8_totitle, p); - return uv ? uv : utf8_to_uv_chk(p,STRLEN_MAX,0,0); + return uv ? uv : utf8_to_uv(p,STRLEN_MAX,0,0); } UV @@ -946,7 +972,7 @@ Perl_to_utf8_lower(pTHX_ U8 *p) if (!PL_utf8_tolower) PL_utf8_tolower = swash_init("utf8", "ToLower", &PL_sv_undef, 4, 0); uv = swash_fetch(PL_utf8_tolower, p); - return uv ? uv : utf8_to_uv_chk(p,STRLEN_MAX,0,0); + return uv ? uv : utf8_to_uv(p,STRLEN_MAX,0,0); } /* a "swash" is a swatch hash */ @@ -1036,7 +1062,7 @@ Perl_swash_fetch(pTHX_ SV *sv, U8 *ptr) PUSHMARK(SP); EXTEND(SP,3); PUSHs((SV*)sv); - PUSHs(sv_2mortal(newSViv(utf8_to_uv_chk(ptr, STRLEN_MAX, 0, 0) & ~(needents - 1)))); + PUSHs(sv_2mortal(newSViv(utf8_to_uv(ptr, STRLEN_MAX, 0, 0) & ~(needents - 1)))); PUSHs(sv_2mortal(newSViv(needents))); PUTBACK; if (call_method("SWASHGET", G_SCALAR))