File-Raw-XML

 view release on metacpan or  search on metacpan

include/frx/frx_enc.h  view on Meta::CPAN

 * still names the byte the user can find with a hex editor. Anything
 * beyond these four is decision G's territory: the caller transcodes on
 * its side and says `encoding => 'UTF-8'`.
 *
 * Detection is XML 1.0 appendix F, in this order: a byte order mark; the
 * first four bytes for the `<?xm` pattern in each width; else UTF-8. A
 * UTF-8-compatible start is then told apart by peeking at the encoding
 * declaration's name, which is ASCII in every encoding this accepts, so
 * a document declaring iso-8859-1 is transcoded from it. The lexer
 * afterwards checks the declaration it parses against what was detected
 * (frx_lex.h): a BOM contradicted by the declaration is fatal, and so is
 * a UTF-16 document declaring anything but UTF-16. UCS-4 and EBCDIC are
 * detected and refused by name.
 *
 * The output is sized once from the bounds: UTF-16 to UTF-8 is at most
 * 1.5x, Latin-1 at most 2x, so max_bytes is checked on the input and the
 * output cannot exceed twice it.
 *
 * The map is one checkpoint per FRX_ENC_STEP input bytes recording the
 * matching output offset; mapping an output offset back re-walks from the
 * nearest checkpoint. The error path pays; the parse path does not.
 *
 * Under strict none of this runs: the 0.01 checks in frx_lex.h refuse
 * UTF-16 by BOM or first pair and every declared non-UTF-8 encoding.
 *
 * Needs frx_err.h, frx_utf8.h. */

enum {
    FRX_ENC_UTF8 = 0,
    FRX_ENC_UTF16LE,
    FRX_ENC_UTF16BE,
    FRX_ENC_LATIN1,
    FRX_ENC_ASCII,
    FRX_ENC_UNSUPPORTED             /* detected, refused by name */

include/frx/frx_enc.h  view on Meta::CPAN

}

/* an encoding name, case-insensitively, to a kind; -1 when not one of ours.
 * The names are the IANA ones and their common aliases. */
static int
frx_enc_kind_by_name(const char *v, size_t n)
{
    if (frx_enc_ieq(v, n, "utf-8") || frx_enc_ieq(v, n, "utf8"))           return FRX_ENC_UTF8;
    if (frx_enc_ieq(v, n, "utf-16le"))                                       return FRX_ENC_UTF16LE;
    if (frx_enc_ieq(v, n, "utf-16be"))                                       return FRX_ENC_UTF16BE;
    if (frx_enc_ieq(v, n, "utf-16"))                                         return FRX_ENC_UTF16LE;   /* the endianness comes from the BOM */
    if (frx_enc_ieq(v, n, "iso-8859-1") || frx_enc_ieq(v, n, "iso_8859-1")
        || frx_enc_ieq(v, n, "latin1") || frx_enc_ieq(v, n, "l1"))           return FRX_ENC_LATIN1;
    if (frx_enc_ieq(v, n, "us-ascii") || frx_enc_ieq(v, n, "ascii")
        || frx_enc_ieq(v, n, "ansi_x3.4-1968"))                              return FRX_ENC_ASCII;
    return -1;
}

/* is the declared name consistent with what was detected? "utf-16" is
 * consistent with either endianness; "utf-16le" only with LE. */
static int

include/frx/frx_enc.h  view on Meta::CPAN

static int
frx_enc_detect(frx_enc *e, const unsigned char *in, size_t len, const char *override, frx_err *err)
{
    const char *v;
    size_t n;

    e->kind = FRX_ENC_UTF8;
    e->bom  = 0;
    e->override = 0;

    /* a BOM is consumed whatever else is decided */
    if (len >= 3 && in[0] == 0xEF && in[1] == 0xBB && in[2] == 0xBF) { e->kind = FRX_ENC_UTF8; e->bom = 3; }
    else if (len >= 4 && in[0] == 0 && in[1] == 0 && in[2] == 0xFE && in[3] == 0xFF)      { e->kind = FRX_ENC_UNSUPPORTED; }
    else if (len >= 4 && in[0] == 0xFF && in[1] == 0xFE && in[2] == 0 && in[3] == 0)      { e->kind = FRX_ENC_UNSUPPORTED; }
    else if (len >= 2 && in[0] == 0xFE && in[1] == 0xFF) { e->kind = FRX_ENC_UTF16BE; e->bom = 2; }
    else if (len >= 2 && in[0] == 0xFF && in[1] == 0xFE) { e->kind = FRX_ENC_UTF16LE; e->bom = 2; }

    if (override) {
        int k = frx_enc_kind_by_name(override, strlen(override));
        if (k < 0) return (frx_err_set(err, FRX_E_ENCODING, 0, "the encoding named by the caller is not one this parser supports"), 0);
        if (frx_enc_ieq(override, strlen(override), "utf-16") && e->bom == 0)
            k = FRX_ENC_UTF16BE;                    /* RFC 2781: no BOM, big-endian */
        if (e->kind == FRX_ENC_UNSUPPORTED) e->kind = k;
        else if (e->bom && k != e->kind && !(e->kind == FRX_ENC_UTF16LE && frx_enc_ieq(override, strlen(override), "utf-16")))
            return (frx_err_set(err, FRX_E_ENCODING, 0, "the encoding named by the caller contradicts the byte order mark"), 0);
        else e->kind = k;
        e->override = 1;
        return 1;
    }

    if (e->kind == FRX_ENC_UNSUPPORTED)
        return (frx_err_set(err, FRX_E_ENCODING, 0, "UCS-4 is not supported; transcode to UTF-8 or UTF-16 first"), 0);
    if (e->bom) return 1;

    /* no BOM: the first four bytes */
    if (len >= 4) {
        if (in[0] == 0 && in[1] == '<' && in[2] == 0 && in[3] == '?')      { e->kind = FRX_ENC_UTF16BE; return 1; }
        if (in[0] == '<' && in[1] == 0 && in[2] == '?' && in[3] == 0)      { e->kind = FRX_ENC_UTF16LE; return 1; }
        if (in[0] == 0 && in[1] == 0 && in[2] == 0 && in[3] == '<')
            return (frx_err_set(err, FRX_E_ENCODING, 0, "UCS-4 is not supported; transcode to UTF-8 or UTF-16 first"), 0);
        if (in[0] == '<' && in[1] == 0 && in[2] == 0 && in[3] == 0)
            return (frx_err_set(err, FRX_E_ENCODING, 0, "UCS-4 is not supported; transcode to UTF-8 or UTF-16 first"), 0);
        if (in[0] == 0x4C && in[1] == 0x6F && in[2] == 0xA7 && in[3] == 0x94)
            return (frx_err_set(err, FRX_E_ENCODING, 0, "EBCDIC is not supported; transcode to UTF-8 first"), 0);
    }

include/frx/frx_enc.h  view on Meta::CPAN

    case FRX_ENC_ASCII:
        if (in[pos] >= 0x80) { *what = "a byte outside US-ASCII in input declared US-ASCII"; return 0; }
        *cp = in[pos];
        return 1;
    default:
        *cp = in[pos];
        return 1;
    }
}

/* Transcode the input after the BOM into e->out. For UTF-8 nothing is
 * allocated: out stays NULL and the caller uses the input past the BOM.
 * 0 on refusal with err set at the input offset. */
static int
frx_enc_run(frx_enc *e, const unsigned char *in, size_t len, frx_err *err)
{
    size_t pos, o = 0, cap, next_cp;
    const char *what = NULL;

    e->out = NULL; e->out_len = 0; e->map = NULL; e->n_map = 0;
    if (e->kind == FRX_ENC_UTF8) return 1;

include/frx/frx_lex.h  view on Meta::CPAN

 * operates on validated names and decoded strings already in the arena. A
 * pull tokeniser rather than a lexer folded into the parser, so that every
 * refusal is testable through the _lex accessor without a tree, and so that
 * the every-offset test can hit the lexer alone.
 *
 * What it accepts: XML 1.0 with the five predefined entities and numeric
 * character references, in UTF-8. What it refuses, each with the offset it
 * was found at: any DOCTYPE (and any <! that is not a comment or CDATA -
 * one rule removes entity expansion, external entities, the billion laughs
 * and XXE as a class, with no option to allow them); UTF-16 and UTF-32 by
 * BOM or by first pair; a declared encoding other than utf-8; version 1.1;
 * a <?xml declaration anywhere but offset 0; a named reference other than
 * the five; a reference to a non-Char; ]]> in content; a literal < in an
 * attribute value; -- inside a comment; a PI whose target is xml in any
 * case; a Name that is not a QName (at most one colon, both parts
 * non-empty); anything not well-formed.
 *
 * Normalisation, in this order and with this word: line ends first (XML
 * 1.0 section 2.11, CRLF and CR become LF, in text and in attribute
 * values), then attribute values (section 3.3.3: with no DTD every
 * attribute is CDATA, so each LITERAL TAB, LF or CR becomes a space). A

include/frx/frx_lex.h  view on Meta::CPAN

    size_t i;
    if (n != strlen(lit)) return 0;
    for (i = 0; i < n; i++) {
        unsigned char a = v[i], b = (unsigned char)lit[i];
        if (a >= 'A' && a <= 'Z') a = (unsigned char)(a - 'A' + 'a');
        if (a != b) return 0;
    }
    return 1;
}

/* the prolog: max_bytes, the BOMs, the first pair, the declaration. With
 * eof clear it asks for more bytes wherever the declaration may go on. */
#define FRX_PROLOG_NEED(l, what) \
    (((l)->need_more || ((l)->pos >= (l)->len && !(l)->eof)) ? ((l)->need_more = 1, 0) : FRX_LEX_FAIL((l), FRX_E_SYNTAX, at, (what)))

static int
frx_lex_prolog(frx_lex *l)
{
    const unsigned char *in = l->in;
    size_t len = l->len;
    const unsigned char *v;

include/frx_abi.h  view on Meta::CPAN

 * `parse` entry renders that into a mortal SV, because it is the entry a
 * consumer reaches for first and a message is what it wants; every other
 * entry that can fail takes an frx_err * and the consumer renders it with
 * `err_format`, so no entry needs a wrapper to translate one. */

typedef enum {
    FRX_OK = 0,
    FRX_E_NOMEM,          /* an allocation failed */
    FRX_E_TOO_LARGE,      /* over max_bytes */
    FRX_E_TOO_DEEP,       /* over max_depth */
    FRX_E_ENCODING,       /* not UTF-8: a BOM, a declaration, a first pair */
    FRX_E_DOCTYPE,        /* <!DOCTYPE, or any <! that is not -- or [CDATA[ */
    FRX_E_UTF8,           /* a byte sequence that is not UTF-8, or not Char */
    FRX_E_SYNTAX,         /* not well-formed */
    FRX_E_NAME,           /* not a Name, or a QName with the wrong colons */
    FRX_E_REFERENCE,      /* an undeclared entity, or a reference to a non-Char */
    FRX_E_NAMESPACE,      /* unbound prefix, xmlns misuse, a relative URI */
    FRX_E_DUP_ATTR,       /* the same attribute twice, literally or expanded */
    FRX_E_DUP_ID,         /* two elements with one ID value */
    FRX_E_ROOT,           /* no root element, or more than one */
    FRX_E_DTD,            /* full: a markup declaration that is not well-formed */

t/03-lexer.t  view on Meta::CPAN


    is_deeply($rows->[0], ['comment', 21, undef, ' c '],      'comment: offset at its <, body verbatim');
    is_deeply($rows->[1], ['pi', 31, 'pi', 'data'],           'PI: target and data');
    is_deeply($rows->[2], ['start', 42, 'r', [['a', '1']]],   'start tag: name and attributes');
    is_deeply($rows->[3], ['empty', 51, 'e', []],             'empty element: no attributes');
    is_deeply($rows->[4], ['text', 55, undef, 'text'],        'text');
    is_deeply($rows->[5], ['cdata', 59, undef, 'cd'],         'CDATA: flagged, body raw');
    is_deeply($rows->[6], ['end', 73, 'r', undef],            'end tag');
}

# the declaration and the BOM
{
    ok(lex(qq{<?xml version="1.0" encoding="UTF-8" standalone="yes"?><a/>}), 'a full declaration');
    ok(lex(qq{<?xml version='1.0' encoding='utf-8'?><a/>}), 'single quotes, lower-case encoding');
    ok(lex(qq{<?xml  version = "1.0" ?><a/>}), 'whitespace around the pseudo-attributes');
    my $rows = lex("\xEF\xBB\xBF<a/>");
    is($rows->[0][1], 3, 'a UTF-8 BOM is skipped and the first token is at offset 3');
    $rows = lex(qq{<?xml version="1.0"?>\n<a/>});
    is($rows->[0][0], 'text', 'whitespace after the declaration is a text token; the parser drops it');
}

# line ends: 2.11
{
    my $rows = lex("<a>x\r\ny\rz\n</a>");
    is($rows->[1][3], "x\ny\nz\n", 'CRLF and lone CR become LF in text');
    $rows = lex("<a><!--\r\n--></a>");
    is($rows->[1][3], "\n", 'and in a comment');

t/04-refusals.t  view on Meta::CPAN

refuses(qq{<!DOCTYPE a><a/>},         qr/DOCTYPE.* at byte offset 0/,  'DOCTYPE before the root');
refuses(qq{<a><!DOCTYPE a></a>},      qr/DOCTYPE.* at byte offset 3/,  'DOCTYPE inside the root');
refuses(qq{<a/><!DOCTYPE a>},         qr/DOCTYPE.* at byte offset 4/,  'DOCTYPE after the root');
refuses(qq{<!DOCTYPE a [<!ENTITY x "y">]><a/>}, qr/DOCTYPE.* at byte offset 0/, 'DOCTYPE with an internal subset');
refuses(qq{<!ENTITY x "y"><a/>},      qr/DOCTYPE.* at byte offset 0/,  '<!ENTITY on its own');
refuses(qq{<a><!ELEMENT a ANY></a>},  qr/DOCTYPE.* at byte offset 3/,  '<!ELEMENT');
refuses(qq{<a><![INCLUDE[x]]></a>},   qr/DOCTYPE.* at byte offset 3/,  'a conditional section');
refuses(qq{<a><!x></a>},              qr/DOCTYPE.* at byte offset 3/,  'any other <!');

# encodings
refuses("\xFE\xFF\0<\0a\0/\0>",       qr/UTF-16 or UTF-32 byte order mark.* at byte offset 0/, 'UTF-16 BE with a BOM');
refuses("\xFF\xFE<\0a\0/\0>\0",       qr/UTF-16 or UTF-32 byte order mark/, 'UTF-16 LE with a BOM');
refuses("\0\0\xFE\xFF\0\0\0<",        qr/UTF-32 byte order mark/, 'UTF-32 BE with a BOM');
refuses("<\0a\0/\0>\0",               qr/UTF-16 without a byte order mark/, 'UTF-16 LE without a BOM');
refuses("\0<\0a\0/\0>",               qr/UTF-16 without a byte order mark/, 'UTF-16 BE without a BOM');
refuses(qq{<?xml version="1.0" encoding="ISO-8859-1"?><a/>}, qr/only the UTF-8 encoding is accepted at byte offset 0/, 'a declared ISO-8859-1');
refuses(qq{<?xml version="1.0" encoding="UTF-16"?><a/>},     qr/only the UTF-8 encoding/, 'a declared UTF-16');
refuses(qq{<?xml version="1.1"?><a/>},                        qr/only XML version 1.0 is accepted/, 'version 1.1');
refuses(qq{<?xml encoding="UTF-8"?><a/>},                     qr/must start with version/, 'a declaration without version');
refuses(qq{<?xml version="1.0"?},                             qr/unterminated XML declaration/, 'an unterminated declaration');
refuses(qq{<?xml version="1.0" standalone="maybe"?><a/>},     qr/malformed standalone/, 'standalone must be yes or no');

# the reserved PI target
refuses(qq{<a><?xml version="1.0"?></a>}, qr/reserved.* at byte offset 5/, '<?xml after offset 0');
refuses(qq{<a><?XML x?></a>},             qr/reserved/, '<?XML in another case');

t/31-encodings.t  view on Meta::CPAN

#!perl
use 5.010;
use strict;
use warnings;
use Test::More;
use Encode qw(encode);
use File::Raw::XML qw(file_xml_decode);

# The full profile's encodings: UTF-16 by BOM, by first bytes and by
# declaration; ISO-8859-1 and US-ASCII by declaration; the caller's
# override; every contradiction refused; and every refusal naming an
# offset in the caller's bytes. The fixtures are made here from one
# character source, never stored as binary. Under strict nothing of this
# is reached and the 0.01 refusals stand.

sub full   { my ($b, %o) = @_; file_xml_decode($b, profile => 'full', %o) }
sub strict { my ($b, %o) = @_; file_xml_decode($b, %o) }
sub refused_full { my ($b, %o) = @_; my $ok = eval { full($b, %o); 1 }; $ok ? '' : $@ }
sub tree {

t/31-encodings.t  view on Meta::CPAN

}

# one document, with a Latin-1 character literal and the rest by reference
my $body    = qq{<r a="\x{e9}"><b>&#x3C0; &#x1D11E;</b><!-- c --><![CDATA[x]]></r>\n};
my $ascii   = $body;
$ascii =~ s/\x{e9}/&#xE9;/;
sub doc { my ($enc, $text) = @_; qq{<?xml version="1.0" encoding="$enc"?>\n} . ($text // $body) }

my %fixture = (
    'UTF-8'             => encode('UTF-8',    doc('UTF-8')),
    'UTF-8 with BOM'    => "\xEF\xBB\xBF" . encode('UTF-8', doc('UTF-8')),
    'UTF-8 undeclared'  => encode('UTF-8',    $body),
    'UTF-16LE with BOM' => "\xFF\xFE" . encode('UTF-16LE', doc('UTF-16')),
    'UTF-16BE with BOM' => "\xFE\xFF" . encode('UTF-16BE', doc('UTF-16')),
    'UTF-16LE no BOM'   => encode('UTF-16LE', doc('UTF-16LE')),
    'UTF-16BE no BOM'   => encode('UTF-16BE', doc('UTF-16BE')),
    'ISO-8859-1'        => encode('ISO-8859-1', doc('ISO-8859-1')),
    'latin1 alias'      => encode('ISO-8859-1', doc('latin1')),
    'US-ASCII'          => encode('US-ASCII',   doc('US-ASCII', $ascii)),
);

# one tree, one canonical form, from every encoding
{
    my $ref = full($fixture{'UTF-8'});
    my $c14n = $ref->c14n(mode => 'exclusive', comments => 1);
    my $shape = tree($ref->document, '');

t/31-encodings.t  view on Meta::CPAN

    for my $name (sort keys %fixture) {
        my $doc = eval { full($fixture{$name}) };
        ok($doc, "$name parses under full") or do { diag $@; next };
        is($doc->c14n(mode => 'exclusive', comments => 1), $c14n, "$name: the same canonical bytes");
        is(tree($doc->document, ''), $shape, "$name: the same tree");
    }
}

# under strict the 0.01 refusals stand
{
    for my $name ('UTF-16LE with BOM', 'UTF-16BE with BOM', 'UTF-16LE no BOM', 'UTF-16BE no BOM') {
        ok(!eval { strict($fixture{$name}); 1 }, "strict refuses $name");
        like($@, qr/UTF-16/, 'naming it');
    }
    ok(!eval { strict($fixture{'ISO-8859-1'}); 1 }, 'strict refuses a declared ISO-8859-1');
    like($@, qr/only the UTF-8 encoding is accepted/, 'with the 0.01 message');
    ok(strict($fixture{'UTF-8 with BOM'}), 'strict still takes a UTF-8 BOM');
}

# 4.3.3: the declaration against what arrived
{
    like(refused_full("\xFF\xFE" . encode('UTF-16LE', doc('UTF-8'))),
         qr/encoding declaration contradicts the byte order mark/, 'a UTF-16 BOM with a declaration of UTF-8 is fatal');
    like(refused_full("\xFF\xFE" . encode('UTF-16LE', doc('UTF-16BE'))),
         qr/contradicts the byte order mark/, 'a little-endian BOM with a declaration of UTF-16BE is fatal');
    ok(full("\xFF\xFE" . encode('UTF-16LE', doc('UTF-16LE'))), 'a little-endian BOM with a declaration of UTF-16LE is fine');
    ok(full("\xFE\xFF" . encode('UTF-16BE', doc('utf-16'))), 'the name is case-insensitive');
    like(refused_full("\xEF\xBB\xBF" . encode('UTF-8', doc('ISO-8859-1'))),
         qr/contradicts the byte order mark/, 'a UTF-8 BOM with a declaration of ISO-8859-1 is fatal');
    like(refused_full(encode('UTF-16LE', doc('ISO-8859-1'))),
         qr/contradicts the encoding the document arrived in/, 'UTF-16 by first bytes with a declaration of ISO-8859-1 is fatal');
    like(refused_full(encode('UTF-8', doc('Shift_JIS'))),
         qr/declared encoding is not one this parser supports/, 'a declaration this parser does not support is refused by name');
    like(refused_full("\0\0\xFE\xFF" . "\0\0\0<"), qr/UCS-4 is not supported/, 'a UCS-4 BOM is refused by name');
    like(refused_full("\0\0\0<\0\0\0?"), qr/UCS-4 is not supported/, 'UCS-4 by first bytes likewise');
    like(refused_full("\x4C\x6F\xA7\x94\x93\x40"), qr/EBCDIC is not supported/, 'and EBCDIC');
}

# the caller's override
{
    my $latin = encode('ISO-8859-1', qq{<r a="\x{e9}"/>});
    like(refused_full($latin), qr/not UTF-8/, 'a Latin-1 byte with no declaration is not UTF-8');
    is(full($latin, encoding => 'ISO-8859-1')->root->attr('a'), "\x{e9}", 'encoding => ISO-8859-1 makes it Latin-1');
    is(full($latin, encoding => 'latin1')->root->attr('a'), "\x{e9}", 'by alias too');
    ok(full(encode('UTF-8', doc('ISO-8859-1', '<r/>')), encoding => 'UTF-8'), 'an override wins over the declaration');
    like(refused_full('<r/>', encoding => 'Shift_JIS'), qr/named by the caller is not one this parser supports/, 'an unknown override is refused');
    like(refused_full("\xFF\xFE" . encode('UTF-16LE', '<r/>'), encoding => 'UTF-8'), qr/named by the caller contradicts the byte order mark/, 'an override contradicting a BOM is refused');
    ok(full("\xFF\xFE" . encode('UTF-16LE', '<r/>'), encoding => 'UTF-16'), 'utf-16 with a BOM takes the BOM\'s endianness');
    ok(full(encode('UTF-16BE', '<r/>'), encoding => 'UTF-16'), 'utf-16 without a BOM is big-endian, RFC 2781');
    ok(!eval { strict('<r/>', encoding => 'UTF-8'); 1 }, 'strict refuses the option rather than ignore it');
    like($@, qr/encoding is an option of profile => 'full'/, 'saying which profile has it');
}

# refusals name the caller's bytes
{
    # an unpaired high surrogate: RFC 2781 section 2.2 forbids a high
    # surrogate not followed by a low one
    my $bad = "\xFF\xFE" . encode('UTF-16LE', '<r>ab') . "\x00\xD8" . encode('UTF-16LE', 'c</r>');
    my $off = 2 + 2 * length('<r>ab');
    like(refused_full($bad), qr/high surrogate not followed by a low surrogate in UTF-16 input at byte offset $off of the UTF-16LE input near "/,
         'an unpaired high surrogate, at its input offset, naming the input');
    $bad = "\xFF\xFE" . encode('UTF-16LE', '<r>a') . "\x00\xDC" . encode('UTF-16LE', '</r>');
    like(refused_full($bad), qr/unpaired low surrogate.* at byte offset 10 of the UTF-16LE input/, 'an unpaired low surrogate, after the BOM and four units');
    $bad = "\xFE\xFF" . encode('UTF-16BE', '<r/>') . "\x00";
    like(refused_full($bad), qr/odd trailing byte in UTF-16 input at byte offset 10 of the UTF-16BE input/, 'an odd trailing byte');
    like(refused_full(encode('UTF-8', doc('US-ASCII', '<r>')) . "\xE9</r>"),
         qr/byte outside US-ASCII in input declared US-ASCII at byte offset (\d+) of the US-ASCII input/, 'a byte outside US-ASCII');

    # a well-formedness error inside a transcoded document maps back
    my $doc16 = "\xFF\xFE" . encode('UTF-16LE', '<a><b></a>');
    my $msg = refused_full($doc16);
    my $want = 2 + 2 * index('<a><b></a>', '</a>');
    like($msg, qr/end tag does not match the open element at byte offset $want of the UTF-16LE input near "/, 'a mismatched end tag in UTF-16 reports the input offset of the tag');
    like($msg, qr/near "<\\x00\/\\x00a\\x00>\\x00"/, 'and the context shows the UTF-16 bytes at that offset');

    # past the first checkpoint
    my $long = '<r>' . ('x' x 5000) . '<b></r>';
    $msg = refused_full("\xFF\xFE" . encode('UTF-16LE', $long));
    $want = 2 + 2 * index($long, '</r>');
    like($msg, qr/at byte offset $want of the UTF-16LE input/, 'the offset map re-walks from a checkpoint past 4 KiB of input');

    # a UTF-8 input with a BOM under full reports offsets in the caller's bytes too
    $msg = refused_full("\xEF\xBB\xBF<a><b></a>");
    like($msg, qr/at byte offset 9 near "/, 'a UTF-8 BOM is counted in the reported offset');
}

# max_bytes is checked on the input
{
    my $b = "\xFF\xFE" . encode('UTF-16LE', '<r/>');
    ok(full($b, max_bytes => length $b), 'max_bytes equal to the input length passes');
    like(refused_full($b, max_bytes => length($b) - 1), qr/input exceeds max_bytes/, 'one less refuses');
}

# Latin-1's whole byte range is characters

t/xmlconf/xmlconf/eduni/errata-2e/errata2e.xml  view on Meta::CPAN


<TEST RECOMMENDATION="XML1.0-errata2e" SECTIONS="E20" URI="E20.xml" ID="rmt-e2e-20" TYPE="invalid">
Tokens, after normalization, must be separated by space, not other
whitespace characters
</TEST>

<!-- E21 defines "internal subset" to not include the square brackets,
     but I don't see any way to test this in a document -->

<TEST RECOMMENDATION="XML1.0-errata2e" SECTIONS="E22" URI="E22.xml" ID="rmt-e2e-22" TYPE="valid">
UTF-8 entities may start with a BOM
</TEST>

<!-- E23 cannot be tested in a standalone test suite -->

<TEST RECOMMENDATION="XML1.0-errata2e" SECTIONS="E24" URI="E24.xml" ID="rmt-e2e-24" TYPE="valid">
Either the built-in entity or a character reference can be used to
represent greater-than after two close-square-brackets
</TEST>

<!-- E25 and E26 cannot be tested in a standalone test suite -->

t/xmlconf/xmlconf/eduni/misc/ht-bh.xml  view on Meta::CPAN

</TEST>
<TEST SECTIONS="3.1 [41]" URI="005.xml" ID="hst-bh-005" TYPE="invalid">
xmlns:xml is an attribute as far as validation is concerned and must
be declared
</TEST>
<TEST SECTIONS="3.1 [41]" URI="006.xml" ID="hst-bh-006" TYPE="invalid">
xmlns:foo is an attribute as far as validation is concerned and must
be declared
</TEST>
<TEST SECTIONS="4.3.3" URI="007.xml" ID="hst-lhs-007" TYPE="not-wf">
UTF-8 BOM plus xml decl of iso-8859-1 incompatible
</TEST>
<TEST SECTIONS="4.3.3" URI="008.xml" ID="hst-lhs-008" TYPE="not-wf">
UTF-16 BOM plus xml decl of utf-8 (using UTF-16 coding) incompatible
</TEST>
<TEST SECTIONS="4.3.3" URI="009.xml" ID="hst-lhs-009" TYPE="not-wf">
UTF-16 BOM plus xml decl of utf-8 (using UTF-8 coding) incompatible
</TEST>

</TESTCASES>



( run in 2.799 seconds using v1.01-cache-2.11-cpan-364913b4093 )