/* utf8.c
*
- * Copyright (c) 1998-2001, Larry Wall
+ * Copyright (c) 1998-2002, Larry Wall
*
* You may distribute under the terms of either the GNU General Public
* License or the Artistic License, as specified in the README file.
#define PERL_IN_UTF8_C
#include "perl.h"
-/* Unicode support */
+/*
+=head1 Unicode Support
-/*
=for apidoc A|U8 *|uvuni_to_utf8_flags|U8 *d|UV uv|UV flags
Adds the UTF8 representation of the Unicode codepoint C<uv> to the end
U8 *
Perl_uvuni_to_utf8_flags(pTHX_ U8 *d, UV uv, UV flags)
{
- if (ckWARN_d(WARN_UTF8)) {
+ if (ckWARN(WARN_UTF8)) {
if (UNICODE_IS_SURROGATE(uv) &&
!(flags & UNICODE_ALLOW_SURROGATE))
- Perl_warner(aTHX_ WARN_UTF8, "UTF-16 surrogate 0x%04"UVxf, uv);
+ Perl_warner(aTHX_ packWARN(WARN_UTF8), "UTF-16 surrogate 0x%04"UVxf, uv);
else if (
((uv >= 0xFDD0 && uv <= 0xFDEF &&
!(flags & UNICODE_ALLOW_FDD0))
((uv <= PERL_UNICODE_MAX) ||
!(flags & UNICODE_ALLOW_SUPER))
)
- Perl_warner(aTHX_ WARN_UTF8,
+ Perl_warner(aTHX_ packWARN(WARN_UTF8),
"Unicode character 0x%04"UVxf" is illegal", uv);
}
if (UNI_IS_INVARIANT(uv)) {
char *s = SvPVX(sv);
if (PL_op)
- Perl_warner(aTHX_ WARN_UTF8,
+ Perl_warner(aTHX_ packWARN(WARN_UTF8),
"%s in %s", s, OP_DESC(PL_op));
else
- Perl_warner(aTHX_ WARN_UTF8, "%s", s);
+ Perl_warner(aTHX_ packWARN(WARN_UTF8), "%s", s);
}
}
Perl_is_uni_alnum(pTHX_ UV c)
{
U8 tmpbuf[UTF8_MAXLEN+1];
- uvchr_to_utf8(tmpbuf, (UV)c);
+ uvchr_to_utf8(tmpbuf, c);
return is_utf8_alnum(tmpbuf);
}
Perl_is_uni_alnumc(pTHX_ UV c)
{
U8 tmpbuf[UTF8_MAXLEN+1];
- uvchr_to_utf8(tmpbuf, (UV)c);
+ uvchr_to_utf8(tmpbuf, c);
return is_utf8_alnumc(tmpbuf);
}
Perl_is_uni_idfirst(pTHX_ UV c)
{
U8 tmpbuf[UTF8_MAXLEN+1];
- uvchr_to_utf8(tmpbuf, (UV)c);
+ uvchr_to_utf8(tmpbuf, c);
return is_utf8_idfirst(tmpbuf);
}
Perl_is_uni_alpha(pTHX_ UV c)
{
U8 tmpbuf[UTF8_MAXLEN+1];
- uvchr_to_utf8(tmpbuf, (UV)c);
+ uvchr_to_utf8(tmpbuf, c);
return is_utf8_alpha(tmpbuf);
}
Perl_is_uni_ascii(pTHX_ UV c)
{
U8 tmpbuf[UTF8_MAXLEN+1];
- uvchr_to_utf8(tmpbuf, (UV)c);
+ uvchr_to_utf8(tmpbuf, c);
return is_utf8_ascii(tmpbuf);
}
Perl_is_uni_space(pTHX_ UV c)
{
U8 tmpbuf[UTF8_MAXLEN+1];
- uvchr_to_utf8(tmpbuf, (UV)c);
+ uvchr_to_utf8(tmpbuf, c);
return is_utf8_space(tmpbuf);
}
Perl_is_uni_digit(pTHX_ UV c)
{
U8 tmpbuf[UTF8_MAXLEN+1];
- uvchr_to_utf8(tmpbuf, (UV)c);
+ uvchr_to_utf8(tmpbuf, c);
return is_utf8_digit(tmpbuf);
}
Perl_is_uni_upper(pTHX_ UV c)
{
U8 tmpbuf[UTF8_MAXLEN+1];
- uvchr_to_utf8(tmpbuf, (UV)c);
+ uvchr_to_utf8(tmpbuf, c);
return is_utf8_upper(tmpbuf);
}
Perl_is_uni_lower(pTHX_ UV c)
{
U8 tmpbuf[UTF8_MAXLEN+1];
- uvchr_to_utf8(tmpbuf, (UV)c);
+ uvchr_to_utf8(tmpbuf, c);
return is_utf8_lower(tmpbuf);
}
Perl_is_uni_cntrl(pTHX_ UV c)
{
U8 tmpbuf[UTF8_MAXLEN+1];
- uvchr_to_utf8(tmpbuf, (UV)c);
+ uvchr_to_utf8(tmpbuf, c);
return is_utf8_cntrl(tmpbuf);
}
Perl_is_uni_graph(pTHX_ UV c)
{
U8 tmpbuf[UTF8_MAXLEN+1];
- uvchr_to_utf8(tmpbuf, (UV)c);
+ uvchr_to_utf8(tmpbuf, c);
return is_utf8_graph(tmpbuf);
}
Perl_is_uni_print(pTHX_ UV c)
{
U8 tmpbuf[UTF8_MAXLEN+1];
- uvchr_to_utf8(tmpbuf, (UV)c);
+ uvchr_to_utf8(tmpbuf, c);
return is_utf8_print(tmpbuf);
}
Perl_is_uni_punct(pTHX_ UV c)
{
U8 tmpbuf[UTF8_MAXLEN+1];
- uvchr_to_utf8(tmpbuf, (UV)c);
+ uvchr_to_utf8(tmpbuf, c);
return is_utf8_punct(tmpbuf);
}
Perl_is_uni_xdigit(pTHX_ UV c)
{
U8 tmpbuf[UTF8_MAXLEN_UCLC+1];
- uvchr_to_utf8(tmpbuf, (UV)c);
+ uvchr_to_utf8(tmpbuf, c);
return is_utf8_xdigit(tmpbuf);
}
UV
Perl_to_uni_upper(pTHX_ UV c, U8* p, STRLEN *lenp)
{
- U8 tmpbuf[UTF8_MAXLEN_UCLC+1];
- uvchr_to_utf8(tmpbuf, (UV)c);
- return to_utf8_upper(tmpbuf, p, lenp);
+ uvchr_to_utf8(p, c);
+ return to_utf8_upper(p, p, lenp);
}
UV
Perl_to_uni_title(pTHX_ UV c, U8* p, STRLEN *lenp)
{
- U8 tmpbuf[UTF8_MAXLEN_UCLC+1];
- uvchr_to_utf8(tmpbuf, (UV)c);
- return to_utf8_title(tmpbuf, p, lenp);
+ uvchr_to_utf8(p, c);
+ return to_utf8_title(p, p, lenp);
}
UV
Perl_to_uni_lower(pTHX_ UV c, U8* p, STRLEN *lenp)
{
- U8 tmpbuf[UTF8_MAXLEN_UCLC+1];
- uvchr_to_utf8(tmpbuf, (UV)c);
- return to_utf8_lower(tmpbuf, p, lenp);
+ uvchr_to_utf8(p, c);
+ return to_utf8_lower(p, p, lenp);
}
UV
Perl_to_uni_fold(pTHX_ UV c, U8* p, STRLEN *lenp)
{
- U8 tmpbuf[UTF8_MAXLEN_FOLD+1];
- uvchr_to_utf8(tmpbuf, (UV)c);
- return to_utf8_fold(tmpbuf, p, lenp);
+ uvchr_to_utf8(p, c);
+ return to_utf8_fold(p, p, lenp);
}
/* for now these all assume no locale info available for Unicode > 255 */
conversion result to. The "lenp" is a pointer to the length
of the result.
-The "swash" is a pointer to the swash to use.
+The "swashp" is a pointer to the swash to use.
-The "normal" is a string like "ToLower" which means the swash
-$utf8::ToLower, which is stored in lib/unicore/To/Lower.pl,
-and loaded by SWASHGET, using lib/utf8_heavy.pl.
+Both the special and normal mappings are stored lib/unicore/To/Foo.pl,
+and loaded by SWASHGET, using lib/utf8_heavy.pl. The special (usually,
+but not always, a multicharacter mapping), is tried first.
-The "special" is a string like "utf8::ToSpecLower", which means
-the hash %utf8::ToSpecLower, which is stored in the same file,
-lib/unicore/To/Lower.pl, and also loaded by SWASHGET. The access
-to the hash is by Perl_to_utf8_case().
+The "special" is a string like "utf8::ToSpecLower", which means the
+hash %utf8::ToSpecLower. The access to the hash is through
+Perl_to_utf8_case().
-=cut
- */
+The "normal" is a string like "ToLower" which means the swash
+%utf8::ToLower.
+
+=cut */
UV
-Perl_to_utf8_case(pTHX_ U8 *p, U8* ustrp, STRLEN *lenp, SV **swashp,char *normal, char *special)
+Perl_to_utf8_case(pTHX_ U8 *p, U8* ustrp, STRLEN *lenp, SV **swashp, char *normal, char *special)
{
- UV uv;
+ UV uv0, uv1;
+ U8 tmpbuf[UTF8_MAXLEN_FOLD+1];
+ STRLEN len = 0;
- if (!*swashp)
- *swashp = swash_init("utf8", normal, &PL_sv_undef, 4, 0);
- uv = swash_fetch(*swashp, p, TRUE);
- if (uv)
- uv = UNI_TO_NATIVE(uv);
- else {
+ uv0 = utf8_to_uvchr(p, 0);
+ /* The NATIVE_TO_UNI() and UNI_TO_NATIVE() mappings
+ * are necessary in EBCDIC, they are redundant no-ops
+ * in ASCII-ish platforms, and hopefully optimized away. */
+ uv1 = NATIVE_TO_UNI(uv0);
+ uvuni_to_utf8(tmpbuf, uv1);
+
+ if (!*swashp) /* load on-demand */
+ *swashp = swash_init("utf8", normal, &PL_sv_undef, 4, 0);
+
+ if (special) {
+ /* It might be "special" (sometimes, but not always,
+ * a multicharacter mapping) */
HV *hv;
SV *keysv;
HE *he;
-
- uv = utf8_to_uvchr(p, 0);
-
+ SV *val;
+
if ((hv = get_hv(special, FALSE)) &&
- (keysv = sv_2mortal(Perl_newSVpvf(aTHX_ "%04"UVXf, uv))) &&
- (he = hv_fetch_ent(hv, keysv, FALSE, 0))) {
- SV *val = HeVAL(he);
- char *s = SvPV(val, *lenp);
- U8 c = *(U8*)s;
- if (*lenp > 1 || UNI_IS_INVARIANT(c))
- Copy(s, ustrp, *lenp, U8);
+ (keysv = sv_2mortal(Perl_newSVpvf(aTHX_ "%04"UVXf, uv1))) &&
+ (he = hv_fetch_ent(hv, keysv, FALSE, 0)) &&
+ (val = HeVAL(he))) {
+ char *s;
+
+ s = SvPV(val, len);
+ if (len == 1)
+ len = uvuni_to_utf8(ustrp, NATIVE_TO_UNI(*(U8*)s)) - ustrp;
else {
- /* something in the 0x80..0xFF range */
- ustrp[0] = UTF8_EIGHT_BIT_HI(c);
- ustrp[1] = UTF8_EIGHT_BIT_LO(c);
- *lenp = 2;
+#ifdef EBCDIC
+ /* If we have EBCDIC we need to remap the characters
+ * since any characters in the low 256 are Unicode
+ * code points, not EBCDIC. */
+ U8 *t = (U8*)s, *tend = t + len, *d;
+
+ d = tmpbuf;
+ if (SvUTF8(val)) {
+ STRLEN tlen = 0;
+
+ while (t < tend) {
+ UV c = utf8_to_uvchr(t, &tlen);
+ if (tlen > 0) {
+ d = uvchr_to_utf8(d, UNI_TO_NATIVE(c));
+ t += tlen;
+ }
+ else
+ break;
+ }
+ }
+ else {
+ while (t < tend) {
+ d = uvchr_to_utf8(d, UNI_TO_NATIVE(*t));
+ t++;
+ }
+ }
+ len = d - tmpbuf;
+ Copy(tmpbuf, ustrp, len, U8);
+#else
+ Copy(s, ustrp, len, U8);
+#endif
}
- return 0;
}
}
+
+ if (!len && *swashp) {
+ UV uv2 = swash_fetch(*swashp, tmpbuf, TRUE);
+
+ if (uv2) {
+ /* It was "normal" (a single character mapping). */
+ UV uv3 = UNI_TO_NATIVE(uv2);
+
+ len = uvchr_to_utf8(ustrp, uv3) - ustrp;
+ }
+ }
+
+ if (!len) /* Neither: just copy. */
+ len = uvchr_to_utf8(ustrp, uv0) - ustrp;
+
if (lenp)
- *lenp = UNISKIP(uv);
- uvuni_to_utf8(ustrp, uv);
- return uv;
+ *lenp = len;
+
+ return len ? utf8_to_uvchr(ustrp, 0) : 0;
}
+/*
+=for apidoc A|UV|to_utf8_upper|U8 *p|U8 *ustrp|STRLEN *lenp
+
+Convert the UTF-8 encoded character at p to its uppercase version and
+store that in UTF-8 in ustrp and its length in bytes in lenp. Note
+that the ustrp needs to be at least UTF8_MAXLEN_UCLC+1 bytes since the
+uppercase version may be longer than the original character (up to two
+characters).
+
+The first character of the uppercased version is returned
+(but note, as explained above, that there may be more.)
+
+=cut */
+
UV
Perl_to_utf8_upper(pTHX_ U8 *p, U8* ustrp, STRLEN *lenp)
{
&PL_utf8_toupper, "ToUpper", "utf8::ToSpecUpper");
}
+/*
+=for apidoc A|UV|to_utf8_title|U8 *p|U8 *ustrp|STRLEN *lenp
+
+Convert the UTF-8 encoded character at p to its titlecase version and
+store that in UTF-8 in ustrp and its length in bytes in lenp. Note
+that the ustrp needs to be at least UTF8_MAXLEN_UCLC+1 bytes since the
+titlecase version may be longer than the original character (up to two
+characters).
+
+The first character of the titlecased version is returned
+(but note, as explained above, that there may be more.)
+
+=cut */
+
UV
Perl_to_utf8_title(pTHX_ U8 *p, U8* ustrp, STRLEN *lenp)
{
&PL_utf8_totitle, "ToTitle", "utf8::ToSpecTitle");
}
+/*
+=for apidoc A|UV|to_utf8_lower|U8 *p|U8 *ustrp|STRLEN *lenp
+
+Convert the UTF-8 encoded character at p to its lowercase version and
+store that in UTF-8 in ustrp and its length in bytes in lenp. Note
+that the ustrp needs to be at least UTF8_MAXLEN_UCLC+1 bytes since the
+lowercase version may be longer than the original character (up to two
+characters).
+
+The first character of the lowercased version is returned
+(but note, as explained above, that there may be more.)
+
+=cut */
+
UV
Perl_to_utf8_lower(pTHX_ U8 *p, U8* ustrp, STRLEN *lenp)
{
&PL_utf8_tolower, "ToLower", "utf8::ToSpecLower");
}
+/*
+=for apidoc A|UV|to_utf8_fold|U8 *p|U8 *ustrp|STRLEN *lenp
+
+Convert the UTF-8 encoded character at p to its foldcase version and
+store that in UTF-8 in ustrp and its length in bytes in lenp. Note
+that the ustrp needs to be at least UTF8_MAXLEN_FOLD+1 bytes since the
+foldcase version may be longer than the original character (up to
+three characters).
+
+The first character of the foldcased version is returned
+(but note, as explained above, that there may be more.)
+
+=cut */
+
UV
Perl_to_utf8_fold(pTHX_ U8 *p, U8* ustrp, STRLEN *lenp)
{
=cut
*/
-/* On ASCII machines this is normally a macro but we want a
- real function in case XS code wants it
+/* On ASCII machines this is normally a macro but we want
+ a real function in case XS code wants it
*/
#undef Perl_utf8n_to_uvchr
UV
Build to the scalar dsv a displayable version of the string spv,
length len, the displayable version being at most pvlim bytes long
(if longer, the rest is truncated and "..." will be appended).
-The flags argument is currently unused but available for future extensions.
+
+The flags argument can have UNI_DISPLAY_ISPRINT set to display
+isPRINT()able characters as themselves, UNI_DISPLAY_BACKSLASH
+to display the \\[nrfta\\] as the backslashed versions (like '\n')
+(UNI_DISPLAY_BACKSLASH is preferred over UNI_DISPLAY_ISPRINT for \\).
+UNI_DISPLAY_QQ (and its alias UNI_DISPLAY_REGEX) have both
+UNI_DISPLAY_BACKSLASH and UNI_DISPLAY_ISPRINT turned on.
+
The pointer to the PV of the dsv is returned.
=cut */
sv_setpvn(dsv, "", 0);
for (s = (char *)spv, e = s + len; s < e; s += UTF8SKIP(s)) {
UV u;
+ bool ok = FALSE;
+
if (pvlim && SvCUR(dsv) >= pvlim) {
truncated++;
break;
}
u = utf8_to_uvchr((U8*)s, 0);
- Perl_sv_catpvf(aTHX_ dsv, "\\x{%"UVxf"}", u);
+ if (u < 256) {
+ if (!ok && (flags & UNI_DISPLAY_BACKSLASH)) {
+ switch (u & 0xFF) {
+ case '\n':
+ Perl_sv_catpvf(aTHX_ dsv, "\\n"); ok = TRUE; break;
+ case '\r':
+ Perl_sv_catpvf(aTHX_ dsv, "\\r"); ok = TRUE; break;
+ case '\t':
+ Perl_sv_catpvf(aTHX_ dsv, "\\t"); ok = TRUE; break;
+ case '\f':
+ Perl_sv_catpvf(aTHX_ dsv, "\\f"); ok = TRUE; break;
+ case '\a':
+ Perl_sv_catpvf(aTHX_ dsv, "\\a"); ok = TRUE; break;
+ case '\\':
+ Perl_sv_catpvf(aTHX_ dsv, "\\" ); ok = TRUE; break;
+ default: break;
+ }
+ }
+ /* isPRINT() is the locale-blind version. */
+ if (!ok && (flags & UNI_DISPLAY_ISPRINT) && isPRINT(u & 0xFF)) {
+ Perl_sv_catpvf(aTHX_ dsv, "%c", (char)(u & 0xFF));
+ ok = TRUE;
+ }
+ }
+ if (!ok)
+ Perl_sv_catpvf(aTHX_ dsv, "\\x{%"UVxf"}", u);
}
if (truncated)
sv_catpvn(dsv, "...", 3);
=for apidoc A|char *|sv_uni_display|SV *dsv|SV *ssv|STRLEN pvlim|UV flags
Build to the scalar dsv a displayable version of the scalar sv,
-he displayable version being at most pvlim bytes long
+the displayable version being at most pvlim bytes long
(if longer, the rest is truncated and "..." will be appended).
-The flags argument is currently unused but available for future extensions.
+
+The flags argument is as in pv_uni_display().
+
The pointer to the PV of the dsv is returned.
=cut */
}
/*
-=for apidoc A|I32|ibcmp_utf8|const char *s1|bool u1|register I32 len1|const char *s2|bool u2|register I32 len2
+=for apidoc A|I32|ibcmp_utf8|const char *s1|char **pe1|register UV l1|bool u1|const char *s2|char **pe2|register UV l2|bool u2
Return true if the strings s1 and s2 differ case-insensitively, false
if not (if they are equal case-insensitively). If u1 is true, the
string s1 is assumed to be in UTF-8-encoded Unicode. If u2 is true,
-the string s2 is assumed to be in UTF-8-encoded Unicode.
+the string s2 is assumed to be in UTF-8-encoded Unicode. If u1 or u2
+are false, the respective string is assumed to be in native 8-bit
+encoding.
+
+If the pe1 and pe2 are non-NULL, the scanning pointers will be copied
+in there (they will point at the beginning of the I<next> character).
+If the pointers behind pe1 or pe2 are non-NULL, they are the end
+pointers beyond which scanning will not continue under any
+circustances. If the byte lengths l1 and l2 are non-zero, s1+l1 and
+s2+l2 will be used as goal end pointers that will also stop the scan,
+and which qualify towards defining a successful match: all the scans
+that define an explicit length must reach their goal pointers for
+a match to succeed).
For case-insensitiveness, the "casefolding" of Unicode is used
instead of upper/lowercasing both the characters, see
=cut */
I32
-Perl_ibcmp_utf8(pTHX_ const char *s1, bool u1, register I32 len1, const char *s2, bool u2, register I32 len2)
+Perl_ibcmp_utf8(pTHX_ const char *s1, char **pe1, register UV l1, bool u1, const char *s2, char **pe2, register UV l2, bool u2)
{
register U8 *p1 = (U8*)s1;
register U8 *p2 = (U8*)s2;
- register U8 *e1 = p1 + len1;
- register U8 *e2 = p2 + len2;
- STRLEN l1, l2;
- UV c1, c2;
- STRLEN foldlen1, foldlen2;
+ register U8 *e1 = 0, *f1 = 0, *q1 = 0;
+ register U8 *e2 = 0, *f2 = 0, *q2 = 0;
+ STRLEN n1 = 0, n2 = 0;
U8 foldbuf1[UTF8_MAXLEN_FOLD+1];
U8 foldbuf2[UTF8_MAXLEN_FOLD+1];
+ U8 natbuf[1+1];
+ STRLEN foldlen1, foldlen2;
+ bool match;
- while (p1 < e1 && p2 < e2) {
- if (u1) {
- if (p1 + UTF8SKIP(p1) > e1)
- break;
- c1 = utf8_to_uvchr((U8*)p1, &l1);
- } else {
- c1 = NATIVE_TO_UNI(*p1);
- l1 = 1;
+ if (pe1)
+ e1 = *(U8**)pe1;
+ if (e1 == 0 || (l1 && l1 < e1 - (U8*)s1))
+ f1 = (U8*)s1 + l1;
+ if (pe2)
+ e2 = *(U8**)pe2;
+ if (e2 == 0 || (l2 && l2 < e2 - (U8*)s2))
+ f2 = (U8*)s2 + l2;
+
+ if ((e1 == 0 && f1 == 0) || (e2 == 0 && f2 == 0) || (f1 == 0 && f2 == 0))
+ return 1; /* mismatch; possible infinite loop or false positive */
+
+ if (!u1 || !u2)
+ natbuf[1] = 0; /* Need to terminate the buffer. */
+
+ while ((e1 == 0 || p1 < e1) &&
+ (f1 == 0 || p1 < f1) &&
+ (e2 == 0 || p2 < e2) &&
+ (f2 == 0 || p2 < f2)) {
+ if (n1 == 0) {
+ if (u1)
+ to_utf8_fold(p1, foldbuf1, &foldlen1);
+ else {
+ natbuf[0] = *p1;
+ to_utf8_fold(natbuf, foldbuf1, &foldlen1);
+ }
+ q1 = foldbuf1;
+ n1 = foldlen1;
}
- if (u2) {
- if (p2 + UTF8SKIP(p2) > e2)
- break;
- c2 = utf8_to_uvchr((U8*)p2, &l2);
- } else {
- c2 = NATIVE_TO_UNI(*p2);
- l2 = 1;
+ if (n2 == 0) {
+ if (u2)
+ to_utf8_fold(p2, foldbuf2, &foldlen2);
+ else {
+ natbuf[0] = *p2;
+ to_utf8_fold(natbuf, foldbuf2, &foldlen2);
+ }
+ q2 = foldbuf2;
+ n2 = foldlen2;
}
- if (c1 != c2) {
- to_uni_fold(c1, foldbuf1, &foldlen1);
- c1 = utf8_to_uvchr(foldbuf1, 0);
-
- to_uni_fold(c2, foldbuf2, &foldlen2);
- c2 = utf8_to_uvchr(foldbuf2, 0);
-
- if (c1 != c2 || foldlen1 != foldlen2)
- return 1; /* mismatch */
+ while (n1 && n2) {
+ if ( UTF8SKIP(q1) != UTF8SKIP(q2) ||
+ (UTF8SKIP(q1) == 1 && *q1 != *q2) ||
+ memNE((char*)q1, (char*)q2, UTF8SKIP(q1)) )
+ return 1; /* mismatch */
+ n1 -= UTF8SKIP(q1);
+ q1 += UTF8SKIP(q1);
+ n2 -= UTF8SKIP(q2);
+ q2 += UTF8SKIP(q2);
}
- p1 += l1;
- p2 += l2;
+ if (n1 == 0)
+ p1 += u1 ? UTF8SKIP(p1) : 1;
+ if (n2 == 0)
+ p2 += u2 ? UTF8SKIP(p2) : 1;
+
}
- return p1 == e1 && p2 == e2 ? 0 : 1; /* 0 match, 1 mismatch */
+
+ /* A match is defined by all the scans that specified
+ * an explicit length reaching their final goals. */
+ match = (f1 == 0 || p1 == f1) && (f2 == 0 || p2 == f2);
+
+ if (match) {
+ if (pe1)
+ *pe1 = (char*)p1;
+ if (pe2)
+ *pe2 = (char*)p2;
+ }
+
+ return match ? 0 : 1; /* 0 match, 1 mismatch */
}