X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=utf8.c;h=3ab402c49791589f3adbd1e60d4fe7fe1b97b044;hb=880b20b67e23950959b9017ea50a2f9fe4e915a4;hp=76eb932f2dba68bd713241bb22d999b453a00f4e;hpb=12ae5dfcd4fd6f54af051c41b2e122532efce8d3;p=p5sagit%2Fp5-mst-13.2.git diff --git a/utf8.c b/utf8.c index 76eb932..3ab402c 100644 --- a/utf8.c +++ b/utf8.c @@ -134,8 +134,49 @@ Perl_is_utf8_char(pTHX_ U8 *s) return len; } +/* +=for apidoc Am|is_utf8_string|U8 *s|STRLEN len + +Returns true if first C bytes of the given string form valid a UTF8 +string, false otherwise. + +=cut +*/ + +bool +Perl_is_utf8_string(pTHX_ U8 *s, STRLEN len) +{ + U8* x=s; + U8* send=s+len; + int c; + while (x < send) { + c = is_utf8_char(x); + x += c; + if (!c || x > send) + return 0; + } + return 1; +} + +/* +=for apidoc Am|U8* s|utf8_to_uv_chk|I32 *retlen|I32 checking + +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 +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. + +=cut +*/ + UV -Perl_utf8_to_uv(pTHX_ U8* s, I32* retlen) +Perl_utf8_to_uv_chk(pTHX_ U8* s, I32* retlen, bool checking) { UV uv = *s; int len; @@ -146,7 +187,12 @@ Perl_utf8_to_uv(pTHX_ U8* s, I32* retlen) } if (!(uv & 0x40)) { dTHR; - if (ckWARN_d(WARN_UTF8)) + if (checking && retlen) { + *retlen = -1; + return 0; + } + + if (ckWARN_d(WARN_UTF8)) Perl_warner(aTHX_ WARN_UTF8, "Malformed UTF-8 character"); if (retlen) *retlen = 1; @@ -168,7 +214,12 @@ Perl_utf8_to_uv(pTHX_ U8* s, I32* retlen) while (len--) { if ((*s & 0xc0) != 0x80) { dTHR; - if (ckWARN_d(WARN_UTF8)) + if (checking && retlen) { + *retlen = -1; + return 0; + } + + if (ckWARN_d(WARN_UTF8)) Perl_warner(aTHX_ WARN_UTF8, "Malformed UTF-8 character"); if (retlen) *retlen -= len + 1; @@ -180,7 +231,28 @@ Perl_utf8_to_uv(pTHX_ U8* s, I32* retlen) return uv; } -/* utf8_distance(a,b) is intended to be a - b in pointer arithmetic */ +/* +=for apidoc Am|U8* s|utf8_to_uv|I32 *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 +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, an optional UTF8 +warning is produced. + +=cut +*/ + +UV +Perl_utf8_to_uv(pTHX_ U8* s, I32* retlen) +{ + return Perl_utf8_to_uv_chk(aTHX_ s, retlen, 0); +} + +/* utf8_distance(a,b) returns the number of UTF8 characters between + the pointers a and b */ I32 Perl_utf8_distance(pTHX_ U8 *a, U8 *b) @@ -222,19 +294,108 @@ Perl_utf8_hop(pTHX_ U8 *s, I32 off) return s; } -/* XXX NOTHING CALLS THE FOLLOWING TWO ROUTINES YET!!! */ /* - * Convert native or reversed UTF-16 to UTF-8. +=for apidoc Am|U8 *|utf8_to_bytes|U8 *s|STRLEN *len + +Converts a string C of length C from UTF8 into byte encoding. +Unlike C, this over-writes the original string, and +updates len to contain the new length. +Returns zero on failure, setting C to -1. + +=cut +*/ + +U8 * +Perl_utf8_to_bytes(pTHX_ U8* s, STRLEN *len) +{ + dTHR; + U8 *send; + U8 *d; + U8 *save; + + send = s + *len; + d = save = s; + + /* ensure valid UTF8 and chars < 256 before updating string */ + while (s < send) { + U8 c = *s++; + if (c >= 0x80 && + ( (s >= send) || ((*s++ & 0xc0) != 0x80) || ((c & 0xfe) != 0xc2))) { + *len = -1; + return 0; + } + } + s = save; + while (s < send) { + if (*s < 0x80) + *d++ = *s++; + else { + I32 ulen; + *d++ = (U8)utf8_to_uv(s, &ulen); + s += ulen; + } + } + *d = '\0'; + *len = d - save; + return save; +} + +/* +=for apidoc Am|U8 *|bytes_to_utf8|U8 *s|STRLEN *len + +Converts a string C of length C from ASCII into UTF8 encoding. +Returns a pointer to the newly-created string, and sets C to +reflect the new length. + +=cut +*/ + +U8* +Perl_bytes_to_utf8(pTHX_ U8* s, STRLEN *len) +{ + dTHR; + U8 *send; + U8 *d; + U8 *dst; + send = s + (*len); + + Newz(801, d, (*len) * 2 + 1, U8); + dst = d; + + while (s < send) { + if (*s < 0x80) + *d++ = *s++; + else { + UV uv = *s++; + *d++ = (( uv >> 6) | 0xc0); + *d++ = (( uv & 0x3f) | 0x80); + } + } + *d = '\0'; + *len = d-dst; + return dst; +} + +/* + * Convert native (big-endian) or reversed (little-endian) UTF-16 to UTF-8. * * Destination must be pre-extended to 3/2 source. Do not use in-place. * We optimize for native, for obvious reasons. */ U8* -Perl_utf16_to_utf8(pTHX_ U16* p, U8* d, I32 bytelen) +Perl_utf16_to_utf8(pTHX_ U8* p, U8* d, I32 bytelen, I32 *newlen) { - U16* pend = p + bytelen / 2; + U8* pend; + U8* dstart = d; + + if (bytelen & 1) + Perl_croak(aTHX_ "panic: utf16_to_utf8: odd bytelen"); + + pend = p + bytelen; + while (p < pend) { - UV uv = *p++; + UV uv = (p[0] << 8) + p[1]; /* UTF-16BE */ + p += 2; if (uv < 0x80) { *d++ = uv; continue; @@ -246,13 +407,9 @@ Perl_utf16_to_utf8(pTHX_ U16* p, U8* d, I32 bytelen) } if (uv >= 0xd800 && uv < 0xdbff) { /* surrogates */ dTHR; - int low = *p++; - if (low < 0xdc00 || low >= 0xdfff) { - if (ckWARN_d(WARN_UTF8)) - Perl_warner(aTHX_ WARN_UTF8, "Malformed UTF-16 surrogate"); - p--; - uv = 0xfffd; - } + UV low = *p++; + if (low < 0xdc00 || low >= 0xdfff) + Perl_croak(aTHX_ "Malformed UTF-16 surrogate"); uv = ((uv - 0xd800) << 10) + (low - 0xdc00) + 0x10000; } if (uv < 0x10000) { @@ -269,13 +426,14 @@ Perl_utf16_to_utf8(pTHX_ U16* p, U8* d, I32 bytelen) continue; } } + *newlen = d - dstart; return d; } /* Note: this one is slightly destructive of the source. */ U8* -Perl_utf16_to_utf8_reversed(pTHX_ U16* p, U8* d, I32 bytelen) +Perl_utf16_to_utf8_reversed(pTHX_ U8* p, U8* d, I32 bytelen, I32 *newlen) { U8* s = (U8*)p; U8* send = s + bytelen; @@ -285,7 +443,7 @@ Perl_utf16_to_utf8_reversed(pTHX_ U16* p, U8* d, I32 bytelen) s[1] = tmp; s += 2; } - return utf16_to_utf8(p, d, bytelen); + return utf16_to_utf8(p, d, bytelen, newlen); } /* for now these are all defined (inefficiently) in terms of the utf8 versions */ @@ -536,7 +694,10 @@ Perl_is_utf8_alnum(pTHX_ U8 *p) if (!is_utf8_char(p)) return FALSE; if (!PL_utf8_alnum) - PL_utf8_alnum = swash_init("utf8", "IsAlnum", &PL_sv_undef, 0, 0); + /* NOTE: "IsWord", not "IsAlnum", since Alnum is a true + * descendant of isalnum(3), in other words, it doesn't + * contain the '_'. --jhi */ + PL_utf8_alnum = swash_init("utf8", "IsWord", &PL_sv_undef, 0, 0); return swash_fetch(PL_utf8_alnum, p); /* return *p == '_' || is_utf8_alpha(p) || is_utf8_digit(p); */ #ifdef SURPRISINGLY_SLOWER /* probably because alpha is usually true */ @@ -698,7 +859,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(p,0); + return uv ? uv : utf8_to_uv_chk(p,0,0); } UV @@ -709,7 +870,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(p,0); + return uv ? uv : utf8_to_uv_chk(p,0,0); } UV @@ -720,7 +881,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(p,0); + return uv ? uv : utf8_to_uv_chk(p,0,0); } /* a "swash" is a swatch hash */ @@ -730,7 +891,7 @@ Perl_swash_init(pTHX_ char* pkg, char* name, SV *listsv, I32 minbits, I32 none) { SV* retval; char tmpbuf[256]; - dSP; + dSP; if (!gv_stashpv(pkg, 0)) { /* demand load utf8 */ ENTER; @@ -754,7 +915,7 @@ Perl_swash_init(pTHX_ char* pkg, char* name, SV *listsv, I32 minbits, I32 none) if (PL_curcop == &PL_compiling) /* XXX ought to be handled by lex_start */ strncpy(tmpbuf, PL_tokenbuf, sizeof tmpbuf); if (call_method("SWASHNEW", G_SCALAR)) - retval = newSVsv(*PL_stack_sp--); + retval = newSVsv(*PL_stack_sp--); else retval = &PL_sv_undef; LEAVE; @@ -810,11 +971,11 @@ Perl_swash_fetch(pTHX_ SV *sv, U8 *ptr) PUSHMARK(SP); EXTEND(SP,3); PUSHs((SV*)sv); - PUSHs(sv_2mortal(newSViv(utf8_to_uv(ptr, 0) & ~(needents - 1)))); + PUSHs(sv_2mortal(newSViv(utf8_to_uv_chk(ptr, 0, 0) & ~(needents - 1)))); PUSHs(sv_2mortal(newSViv(needents))); PUTBACK; if (call_method("SWASHGET", G_SCALAR)) - retval = newSVsv(*PL_stack_sp--); + retval = newSVsv(*PL_stack_sp--); else retval = &PL_sv_undef; POPSTACK;