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>π 𝄞</b><!-- c --><![CDATA[x]]></r>\n};
my $ascii = $body;
$ascii =~ s/\x{e9}/é/;
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>