require
5;
BEGIN {
*DEBUG
=
sub
() {0}
unless
defined
&DEBUG
}
$VERSION @ISA
@Known_formatting_codes @Known_directives
%Known_formatting_codes %Known_directives
$NL
)
;
@ISA
= (
'Pod::Simple::BlackBox'
);
$VERSION
=
'3.37'
;
@Known_formatting_codes
=
qw(I B C L E F S X Z)
;
%Known_formatting_codes
=
map
((
$_
=>1),
@Known_formatting_codes
);
@Known_directives
=
qw(head1 head2 head3 head4 item over back)
;
%Known_directives
=
map
((
$_
=>
'Plain'
),
@Known_directives
);
$NL
= $/
unless
defined
$NL
;
BEGIN {
if
(
defined
&ASCII
) { }
elsif
(
chr
(65) eq
'A'
) {
*ASCII
=
sub
() {1} }
else
{
*ASCII
=
sub
() {
''
} }
unless
(
defined
&MANY_LINES
) {
*MANY_LINES
=
sub
() {20} }
DEBUG > 4 and
print
STDERR
"MANY_LINES is "
, MANY_LINES(),
"\n"
;
unless
(MANY_LINES() >= 1) {
die
"MANY_LINES is too small ("
, MANY_LINES(),
")!\nAborting"
;
}
if
(
defined
&UNICODE
) { }
elsif
($] >= 5.008) {
*UNICODE
=
sub
() {1} }
else
{
*UNICODE
=
sub
() {
''
} }
}
if
(DEBUG > 2) {
print
STDERR
"# We are "
, ASCII ?
''
:
'not '
,
"in ASCII-land\n"
;
print
STDERR
"# We are under a Unicode-safe Perl.\n"
;
}
if
($] ge 5.007_003) {
$Pod::Simple::nbsp
=
chr
utf8::unicode_to_native(0xA0);
$Pod::Simple::shy
=
chr
utf8::unicode_to_native(0xAD);
}
elsif
(Pod::Simple::ASCII) {
$Pod::Simple::nbsp
=
"\xA0"
;
$Pod::Simple::shy
=
"\xAD"
;
}
else
{
$Pod::Simple::nbsp
=
"\x41"
;
$Pod::Simple::shy
=
"\xCA"
;
}
__PACKAGE__->_accessorize(
'_output_is_for_JustPod'
,
'nbsp_for_S'
,
'source_filename'
,
'source_dead'
,
'output_fh'
,
'hide_line_numbers'
,
'line_count'
,
'pod_para_count'
,
'no_whining'
,
'no_errata_section'
,
'complain_stderr'
,
'doc_has_started'
,
'bare_output'
,
'keep_encoding_directive'
,
'nix_X_codes'
,
'merge_text'
,
'preserve_whitespace'
,
'strip_verbatim_indent'
,
'parse_characters'
,
'content_seen'
,
'errors_seen'
,
'codes_in_verbatim'
,
'code_handler'
,
'cut_handler'
,
'pod_handler'
,
'whiteline_handler'
,
'parse_empty_lists'
,
'raw_mode'
,
);
sub
any_errata_seen {
return
shift
->{
'errors_seen'
} || 0;
}
sub
errata_seen {
return
shift
->{
'all_errata'
} || {};
}
sub
detected_encoding {
return
shift
->{
'detected_encoding'
};
}
sub
encoding {
my
$this
=
shift
;
return
$this
->{
'encoding'
}
unless
@_
;
$this
->_handle_encoding_line(
"=encoding $_[0]"
);
if
(
$this
->{
'_processed_encoding'
}) {
delete
$this
->{
'_processed_encoding'
};
if
(!
$this
->{
'encoding_command_statuses'
} ) {
DEBUG > 2 and
print
STDERR
" CRAZY ERROR: encoding wasn't really handled?!\n"
;
}
elsif
(
$this
->{
'encoding_command_statuses'
}[-1] ) {
$this
->scream(
"=encoding $_[0]"
,
sprintf
"Couldn't do %s: %s"
,
$this
->{
'encoding_command_reqs'
}[-1],
$this
->{
'encoding_command_statuses'
}[-1],
);
}
else
{
DEBUG > 2 and
print
STDERR
" (encoding successfully handled.)\n"
;
}
return
$this
->{
'encoding'
};
}
else
{
return
undef
;
}
}
BEGIN {
*pretty
= \
&Pod::Simple::BlackBox::pretty
;
*stringify_lol
= \
&Pod::Simple::BlackBox::stringify_lol
;
*my_qr
= \
&Pod::Simple::BlackBox::my_qr
;
}
sub
version_report {
my
$class
=
ref
(
$_
[0]) ||
$_
[0];
if
(
$class
eq __PACKAGE__) {
return
"$class $VERSION"
;
}
else
{
my
$v
=
$class
->VERSION;
return
"$class $v ("
. __PACKAGE__ .
" $VERSION)"
;
}
}
sub
output_string {
my
$this
=
shift
;
return
$this
->{
'output_string'
}
unless
@_
;
my
$x
= (
defined
(
$_
[0]) and
ref
(
$_
[0])) ?
$_
[0] : \(
$_
[0] );
$$x
=
''
unless
defined
$$x
;
DEBUG > 4 and
print
STDERR
"# Output string set to $x ($$x)\n"
;
$this
->{
'output_fh'
} = Pod::Simple::TiedOutFH->handle_on(
$_
[0]);
return
$this
->{
'output_string'
} =
$_
[0];
}
sub
abandon_output_string {
$_
[0]->abandon_output_fh;
delete
$_
[0]{
'output_string'
} }
sub
abandon_output_fh {
$_
[0]->output_fh(
undef
) }
sub
new {
my
$class
=
ref
(
$_
[0]) ||
$_
[0];
return
bless
{
'accept_codes'
=> {
map
( (
$_
=>
$_
),
@Known_formatting_codes
) },
'accept_directives'
=> {
%Known_directives
},
'accept_targets'
=> {},
},
$class
;
}
sub
_handle_element_start {
my
(
$self
,
$element_name
,
$attr_hash_r
) =
@_
;
return
;
}
sub
_handle_element_end {
my
(
$self
,
$element_name
) =
@_
;
return
;
}
sub
_handle_text {
my
(
$self
,
$text
) =
@_
;
return
;
}
sub
accept_directive_as_verbatim {
shift
->_accept_directives(
'Verbatim'
,
@_
) }
sub
accept_directive_as_data {
shift
->_accept_directives(
'Data'
,
@_
) }
sub
accept_directive_as_processed {
shift
->_accept_directives(
'Plain'
,
@_
) }
sub
_accept_directives {
my
(
$this
,
$type
) =
splice
@_
,0,2;
foreach
my
$d
(
@_
) {
next
unless
defined
$d
and
length
$d
;
Carp::croak
"\"$d\" isn't a valid directive name"
unless
$d
=~ m/^[a-zA-Z][a-zA-Z0-9]*$/s;
Carp::croak
"\"$d\" is already a reserved Pod directive name"
if
exists
$Known_directives
{
$d
};
$this
->{
'accept_directives'
}{
$d
} =
$type
;
DEBUG > 2 and
print
STDERR
"Learning to accept \"=$d\" as directive of type $type\n"
;
}
DEBUG > 6 and
print
STDERR
"$this\'s accept_directives : "
,
pretty(
$this
->{
'accept_directives'
}),
"\n"
;
return
sort
keys
%{
$this
->{
'accept_directives'
} }
if
wantarray
;
return
;
}
sub
unaccept_directive {
shift
->unaccept_directives(
@_
) };
sub
unaccept_directives {
my
$this
=
shift
;
foreach
my
$d
(
@_
) {
next
unless
defined
$d
and
length
$d
;
Carp::croak
"\"$d\" isn't a valid directive name"
unless
$d
=~ m/^[a-zA-Z][a-zA-Z0-9]*$/s;
Carp::croak
"But you must accept \"$d\" directives -- it's a builtin!"
if
exists
$Known_directives
{
$d
};
delete
$this
->{
'accept_directives'
}{
$d
};
DEBUG > 2 and
print
STDERR
"OK, won't accept \"=$d\" as directive.\n"
;
}
return
sort
keys
%{
$this
->{
'accept_directives'
} }
if
wantarray
;
return
}
sub
accept_target {
shift
->accept_targets(
@_
) }
sub
accept_target_as_text {
shift
->accept_targets_as_text(
@_
) }
sub
accept_targets {
shift
->_accept_targets(
'1'
,
@_
) }
sub
accept_targets_as_text {
shift
->_accept_targets(
'force_resolve'
,
@_
) }
sub
_accept_targets {
my
(
$this
,
$type
) =
splice
@_
,0,2;
foreach
my
$t
(
@_
) {
next
unless
defined
$t
and
length
$t
;
$this
->{
'accept_targets'
}{
$t
} =
$type
;
DEBUG > 2 and
print
STDERR
"Learning to accept \"$t\" as target of type $type\n"
;
}
return
sort
keys
%{
$this
->{
'accept_targets'
} }
if
wantarray
;
return
;
}
sub
unaccept_target {
shift
->unaccept_targets(
@_
) }
sub
unaccept_targets {
my
$this
=
shift
;
foreach
my
$t
(
@_
) {
next
unless
defined
$t
and
length
$t
;
delete
$this
->{
'accept_targets'
}{
$t
};
DEBUG > 2 and
print
STDERR
"OK, won't accept \"$t\" as target.\n"
;
}
return
sort
keys
%{
$this
->{
'accept_targets'
} }
if
wantarray
;
return
;
}
my
$xml_name_re
= my_qr(
'[^-.0-8:A-Z_a-z[:^ascii:]]'
,
'9'
);
$xml_name_re
=
qr/[\x00-\x2C\x2F\x39\x3B-\x40\x5B-\x5E\x60\x7B-\x7F]/
unless
$xml_name_re
;
sub
accept_code {
shift
->accept_codes(
@_
) }
sub
accept_codes {
my
$this
=
shift
;
foreach
my
$new_code
(
@_
) {
next
unless
defined
$new_code
and
length
$new_code
;
Carp::croak
"\"$new_code\" isn't a valid element name"
if
$new_code
=~
$xml_name_re
or
$new_code
=~ m/^[-\.0-9]/s
or
$new_code
=~ m/:[-\.0-9]/s;
$this
->{
'accept_codes'
}{
$new_code
} =
$new_code
;
}
return
;
}
sub
unaccept_code {
shift
->unaccept_codes(
@_
) }
sub
unaccept_codes {
my
$this
=
shift
;
foreach
my
$new_code
(
@_
) {
next
unless
defined
$new_code
and
length
$new_code
;
Carp::croak
"\"$new_code\" isn't a valid element name"
if
$new_code
=~
$xml_name_re
or
$new_code
=~ m/^[-\.0-9]/s
or
$new_code
=~ m/:[-\.0-9]/s;
Carp::croak
"But you must accept \"$new_code\" codes -- it's a builtin!"
if
grep
$new_code
eq
$_
,
@Known_formatting_codes
;
delete
$this
->{
'accept_codes'
}{
$new_code
};
DEBUG > 2 and
print
STDERR
"OK, won't accept the code $new_code<...>.\n"
;
}
return
;
}
sub
parse_string_document {
my
$self
=
shift
;
my
@lines
;
foreach
my
$line_group
(
@_
) {
next
unless
defined
$line_group
and
length
$line_group
;
pos
(
$line_group
) = 0;
while
(
$line_group
=~
m/([^\n\r]*)(\r?\n?)/g
) {
$self
->parse_lines($1)
if
length
($1) or
length
($2)
or
pos
(
$line_group
) !=
length
(
$line_group
);
}
}
$self
->parse_lines(
undef
);
return
$self
;
}
sub
_init_fh_source {
my
(
$self
,
$source
) =
@_
;
return
;
}
sub
parse_file {
my
(
$self
,
$source
) = (
@_
);
if
(!
defined
$source
) {
Carp::croak(
"Can't use empty-string as a source for parse_file"
);
}
elsif
(
ref
(\
$source
) eq
'GLOB'
) {
$self
->{
'source_filename'
} =
''
. (
$source
);
}
elsif
(
ref
$source
) {
$self
->{
'source_filename'
} =
''
. (
$source
);
}
elsif
(!
length
$source
) {
Carp::croak(
"Can't use empty-string as a source for parse_file"
);
}
else
{
{
local
*PODSOURCE
;
open
(PODSOURCE,
"<$source"
) || Carp::croak(
"Can't open $source: $!"
);
$self
->{
'source_filename'
} =
$source
;
$source
=
*PODSOURCE
{IO};
}
$self
->_init_fh_source(
$source
);
}
$self
->{
'source_fh'
} =
$source
;
my
(
$i
,
@lines
);
until
(
$self
->{
'source_dead'
} ) {
splice
@lines
;
for
(
$i
= MANY_LINES;
$i
--;) {
local
$/ =
$NL
;
push
@lines
,
scalar
(<
$source
>);
last
unless
defined
$lines
[-1];
}
my
$at_eof
= !
$lines
[-1];
pop
@lines
if
$at_eof
;
s/\r\n?/\n/g
for
@lines
;
@lines
=
split
(/(?<=\n)/,
join
(
''
,
@lines
));
push
@lines
,
undef
if
$at_eof
;
$self
->parse_lines(
@lines
);
}
delete
(
$self
->{
'source_fh'
});
return
$self
;
}
sub
parse_from_file {
my
(
$self
,
$source
,
$to
) =
@_
;
$self
=
$self
->new
unless
ref
(
$self
);
if
(!
defined
$source
) {
$source
=
*STDIN
{IO}
}
elsif
(
ref
(\
$source
) eq
'GLOB'
) {
}
elsif
(
ref
(
$source
) ) {
}
elsif
(!
length
$source
or
$source
eq
'-'
or
$source
=~ m/^<&(?:STDIN|0)$/i
) {
$source
=
*STDIN
{IO};
}
if
(!
defined
$to
) {
$self
->output_fh(
*STDOUT
{IO} );
}
elsif
(
ref
(\
$to
) eq
'GLOB'
) {
$self
->output_fh(
$to
);
}
elsif
(
ref
(
$to
)) {
$self
->output_fh(
$to
);
}
elsif
(!
length
$to
or
$to
eq
'-'
or
$to
=~ m/^>&?(?:STDOUT|1)$/i
) {
$self
->output_fh(
*STDOUT
{IO} );
}
elsif
(
$to
=~ m/^>&(?:STDERR|2)$/i) {
$self
->output_fh(
*STDERR
{IO} );
}
else
{
my
$out_fh
= Symbol::gensym();
DEBUG and
print
STDERR
"Write-opening to $to\n"
;
open
(
$out_fh
,
">$to"
) or Carp::croak
"Can't write-open $to: $!"
;
binmode
(
$out_fh
)
if
$self
->can(
'write_with_binmode'
) and
$self
->write_with_binmode;
$self
->output_fh(
$out_fh
);
}
return
$self
->parse_file(
$source
);
}
sub
whine {
my
$self
=
shift
(
@_
);
++
$self
->{
'errors_seen'
};
if
(
$self
->{
'no_whining'
}) {
DEBUG > 9 and
print
STDERR
"Discarding complaint (at line $_[0]) $_[1]\n because no_whining is on.\n"
;
return
;
}
push
@{
$self
->{
'all_errata'
}{
$_
[0]}},
$_
[1];
return
$self
->_complain_warn(
@_
)
if
$self
->{
'complain_stderr'
};
return
$self
->_complain_errata(
@_
);
}
sub
scream {
my
$self
=
shift
(
@_
);
++
$self
->{
'errors_seen'
};
push
@{
$self
->{
'all_errata'
}{
$_
[0]}},
$_
[1];
return
$self
->_complain_warn(
@_
)
if
$self
->{
'complain_stderr'
};
return
$self
->_complain_errata(
@_
);
}
sub
_complain_warn {
my
(
$self
,
$line
,
$complaint
) =
@_
;
return
printf
STDERR
"%s around line %s: %s\n"
,
$self
->{
'source_filename'
} ||
'Pod input'
,
$line
,
$complaint
;
}
sub
_complain_errata {
my
(
$self
,
$line
,
$complaint
) =
@_
;
if
(
$self
->{
'no_errata_section'
} ) {
DEBUG > 9 and
print
STDERR
"Discarding erratum (at line $line) $complaint\n because no_errata_section is on.\n"
;
}
else
{
DEBUG > 9 and
print
STDERR
"Queuing erratum (at line $line) $complaint\n"
;
push
@{
$self
->{
'errata'
}{
$line
}},
$complaint
}
return
1;
}
sub
_get_initial_item_type {
my
(
$self
,
$para
) =
@_
;
return
$para
->[1]{
'~type'
}
if
$para
->[1]{
'~type'
};
return
$para
->[1]{
'~type'
} =
'text'
if
join
(
"\n"
, @{
$para
}[2 ..
$#$para
]) =~ m/^\s*(\d+)\.?\s*$/s and $1 ne
'1'
;
return
$self
->_get_item_type(
$para
);
}
sub
_get_item_type {
my
(
$self
,
$para
) =
@_
;
return
$para
->[1]{
'~type'
}
if
$para
->[1]{
'~type'
};
my
$content
=
join
"\n"
, @{
$para
}[2 ..
$#$para
];
if
(
$content
=~ m/^\s*\*\s*$/s or
$content
=~ m/^\s*$/s) {
splice
@$para
, 2;
$para
->[1]{
'~orig_content'
} =
$content
;
return
$para
->[1]{
'~type'
} =
'bullet'
;
}
elsif
(
$content
=~ m/^\s*\*\s+(.+)/s) {
$para
->[1]{
'~orig_content'
} =
$content
;
$para
->[1]{
'~_freaky_para_hack'
} = $1;
DEBUG > 2 and
print
STDERR
" Tolerating $$para[2] as =item *\\n\\n$1\n"
;
splice
@$para
, 2;
return
$para
->[1]{
'~type'
} =
'bullet'
;
}
elsif
(
$content
=~ m/^\s*(\d+)\.?\s*$/s) {
$para
->[1]{
'~orig_content'
} =
$content
;
$para
->[1]{
'number'
} = $1;
splice
@$para
, 2;
return
$para
->[1]{
'~type'
} =
'number'
;
}
else
{
return
$para
->[1]{
'~type'
} =
'text'
;
}
}
sub
_make_treelet {
my
$self
=
shift
;
my
$treelet
;
if
(!
@_
) {
return
[
''
];
}
if
(
ref
$_
[0] and
ref
$_
[0][0] and
$_
[0][0][0] eq
'~Top'
) {
DEBUG and
print
STDERR
"Applying precooked treelet hack to $_[0][0]\n"
;
$treelet
=
$_
[0][0];
splice
@$treelet
, 0, 2;
return
$treelet
;
}
else
{
$treelet
=
$self
->_treelet_from_formatting_codes(
@_
);
}
if
( !
$self
->{
'_output_is_for_JustPod'
}
&&
$self
->_remap_sequences(
$treelet
) )
{
$self
->_treat_Zs(
$treelet
);
$self
->_treat_Ls(
$treelet
);
$self
->_treat_Es(
$treelet
);
$self
->_treat_Ss(
$treelet
);
$self
->_wrap_up(
$treelet
);
}
else
{
DEBUG and
print
STDERR
"Formatless treelet gets fast-tracked.\n"
;
}
splice
@$treelet
, 0, 2;
return
$treelet
;
}
sub
_wrap_up {
my
(
$self
,
@stack
) =
@_
;
my
$nixx
=
$self
->{
'nix_X_codes'
};
my
$merge
=
$self
->{
'merge_text'
};
return
unless
$nixx
or
$merge
;
DEBUG > 2 and
print
STDERR
"\nStarting _wrap_up traversal.\n"
,
$merge
? (
" Merge mode on\n"
) : (),
$nixx
? (
" Nix-X mode on\n"
) : (),
;
my
(
$i
,
$treelet
);
while
(
$treelet
=
shift
@stack
) {
DEBUG > 3 and
print
STDERR
" Considering children of this $treelet->[0] node...\n"
;
for
(
$i
= 2;
$i
<
@$treelet
; ++
$i
) {
DEBUG > 3 and
print
STDERR
" Considering child at $i "
, pretty(
$treelet
->[
$i
]),
"\n"
;
if
(
$nixx
and
ref
$treelet
->[
$i
] and
$treelet
->[
$i
][0] eq
'X'
) {
DEBUG > 3 and
print
STDERR
" Nixing X node at $i\n"
;
splice
(
@$treelet
,
$i
, 1);
redo
;
}
elsif
(
$merge
and
$i
!= 2 and
!
ref
$treelet
->[
$i
] and !
ref
$treelet
->[
$i
- 1]
) {
DEBUG > 3 and
print
STDERR
" Merging "
,
$i
-1,
":[$treelet->[$i-1]] and $i\:[$treelet->[$i]]\n"
;
$treelet
->[
$i
-1] .= (
splice
(
@$treelet
,
$i
, 1) )[0];
DEBUG > 4 and
print
STDERR
" Now: "
,
$i
-1,
":[$treelet->[$i-1]]\n"
;
--
$i
;
next
;
}
elsif
(
ref
$treelet
->[
$i
] ) {
DEBUG > 4 and
print
STDERR
" Enqueuing "
, pretty(
$treelet
->[
$i
]),
" for traversal.\n"
;
push
@stack
,
$treelet
->[
$i
];
if
(
$treelet
->[
$i
][0] eq
'L'
) {
my
$thing
;
foreach
my
$attrname
(
'section'
,
'to'
) {
if
(
defined
(
$thing
=
$treelet
->[
$i
][1]{
$attrname
}) and
ref
$thing
) {
unshift
@stack
,
$thing
;
DEBUG > 4 and
print
STDERR
" +Enqueuing "
,
pretty(
$treelet
->[
$i
][1]{
$attrname
} ),
" as an attribute value to tweak.\n"
;
}
}
}
}
}
}
DEBUG > 2 and
print
STDERR
"End of _wrap_up traversal.\n\n"
;
return
;
}
sub
_remap_sequences {
my
(
$self
,
@stack
) =
@_
;
if
(
@stack
== 1 and @{
$stack
[0] } == 3 and !
ref
$stack
[0][2]) {
DEBUG and
print
STDERR
"Skipping _remap_sequences: formatless treelet.\n"
;
return
0;
}
my
$map
= (
$self
->{
'accept_codes'
} ||
die
"NO accept_codes in $self?!?"
);
my
$start_line
=
$stack
[0][1]{
'start_line'
};
DEBUG > 2 and
printf
"\nAbout to start _remap_sequences on treelet from line %s.\n"
,
$start_line
||
'[?]'
;
DEBUG > 3 and
print
STDERR
" Map: "
,
join
(
'; '
,
map
"$_="
. (
ref
(
$map
->{
$_
}) ?
join
(
","
, @{
$map
->{
$_
}}) :
$map
->{
$_
}
),
sort
keys
%$map
),
(
"B~C~E~F~I~L~S~X~Z"
eq
join
'~'
,
sort
keys
%$map
)
?
" (all normal)\n"
:
"\n"
;
my
(
$is
,
$was
,
$i
,
$treelet
);
while
(
$treelet
=
shift
@stack
) {
DEBUG > 3 and
print
STDERR
" Considering children of this $treelet->[0] node...\n"
;
for
(
$i
= 2;
$i
<
@$treelet
; ++
$i
) {
next
unless
ref
$treelet
->[
$i
];
DEBUG > 4 and
print
STDERR
" Noting child $i : $treelet->[$i][0]<...>\n"
;
$is
=
$treelet
->[
$i
][0] =
$map
->{
$was
=
$treelet
->[
$i
][0] };
if
( DEBUG > 3 ) {
if
(!
defined
$is
) {
print
STDERR
" Code $was<> is UNKNOWN!\n"
;
}
elsif
(
$is
eq
$was
) {
DEBUG > 4 and
print
STDERR
" Code $was<> stays the same.\n"
;
}
else
{
print
STDERR
" Code $was<> maps to "
,
ref
(
$is
)
? (
"tags "
,
map
(
"$_<"
,
@$is
),
'...'
,
map
(
'>'
,
@$is
),
"\n"
)
:
"tag $is<...>.\n"
;
}
}
if
(!
defined
$is
) {
$self
->whine(
$start_line
,
"Deleting unknown formatting code $was<>"
);
$is
=
$treelet
->[
$i
][0] =
'1'
;
}
if
(
ref
$is
) {
my
@dynasty
=
@$is
;
DEBUG > 4 and
print
STDERR
" Renaming $was node to $dynasty[-1]\n"
;
$treelet
->[
$i
][0] =
pop
@dynasty
;
my
$nugget
;
while
(
@dynasty
) {
DEBUG > 4 and
printf
" Grafting a new %s node between %s and %s\n"
,
$dynasty
[-1],
$treelet
->[0],
$treelet
->[
$i
][0],
;
splice
@$treelet
,
$i
, 1, [
pop
(
@dynasty
), {},
$treelet
->[
$i
]];
}
}
elsif
(
$is
eq
'0'
) {
splice
(
@$treelet
,
$i
, 1);
--
$i
;
}
elsif
(
$is
eq
'1'
) {
splice
(
@$treelet
,
$i
, 1
=>
splice
@{
$treelet
->[
$i
] },2
);
--
$i
;
}
else
{
unshift
@stack
,
$treelet
->[
$i
];
}
}
}
DEBUG > 2 and
print
STDERR
"End of _remap_sequences traversal.\n\n"
;
if
(
@_
== 2 and @{
$_
[1] } == 3 and !
ref
$_
[1][2]) {
DEBUG and
print
STDERR
"Noting that the treelet is now formatless.\n"
;
return
0;
}
return
1;
}
sub
_ponder_extend {
my
(
$self
,
$para
) =
@_
;
my
$content
=
join
' '
,
splice
@$para
, 2;
$content
=~ s/^\s+//s;
$content
=~ s/\s+$//s;
DEBUG > 2 and
print
STDERR
"Ogling extensor: =extend $content\n"
;
if
(
$content
=~
m/^
(\S+)
\s+
(\S+)
(?:\s+(\S+))?
\s*
$
/xs
) {
my
$new_letter
= $1;
my
$fallbacks_one
= $2;
my
$elements_one
;
$elements_one
=
defined
($3) ? $3 : $1;
DEBUG > 2 and
print
STDERR
"Extensor has good syntax.\n"
;
unless
(
$new_letter
=~ m/^[A-Z]$/s or
$new_letter
) {
DEBUG > 2 and
print
STDERR
" $new_letter isn't a valid thing to entend.\n"
;
$self
->whine(
$para
->[1]{
'start_line'
},
"You can extend only formatting codes A-Z, not like \"$new_letter\""
);
return
;
}
if
(
grep
$new_letter
eq
$_
,
@Known_formatting_codes
) {
DEBUG > 2 and
print
STDERR
" $new_letter isn't a good thing to extend, because known.\n"
;
$self
->whine(
$para
->[1]{
'start_line'
},
"You can't extend an established code like \"$new_letter\""
);
return
;
}
unless
(
$fallbacks_one
=~ m/^[A-Z](,[A-Z])*$/s
or
$fallbacks_one
eq
'0'
or
$fallbacks_one
eq
'1'
) {
$self
->whine(
$para
->[1]{
'start_line'
},
"Format for second =extend parameter must be like"
.
" M or 1 or 0 or M,N or M,N,O but you have it like "
.
$fallbacks_one
);
return
;
}
unless
(
$elements_one
=~ m/^[^ ,]+(,[^ ,]+)*$/s) {
$self
->whine(
$para
->[1]{
'start_line'
},
"Format for third =extend parameter: like foo or bar,Baz,qu:ux but not like "
.
$elements_one
);
return
;
}
my
@fallbacks
=
split
','
,
$fallbacks_one
, -1;
my
@elements
=
split
','
,
$elements_one
, -1;
foreach
my
$f
(
@fallbacks
) {
next
if
exists
$Known_formatting_codes
{
$f
} or
$f
eq
'0'
or
$f
eq
'1'
;
DEBUG > 2 and
print
STDERR
" Can't fall back on unknown code $f\n"
;
$self
->whine(
$para
->[1]{
'start_line'
},
"Can't use unknown formatting code '$f' as a fallback for '$new_letter'"
);
return
;
}
DEBUG > 3 and
printf
STDERR
"Extensor: Fallbacks <%s> Elements <%s>.\n"
,
@fallbacks
,
@elements
;
my
$canonical_form
;
foreach
my
$e
(
@elements
) {
if
(
exists
$self
->{
'accept_codes'
}{
$e
}) {
DEBUG > 1 and
print
STDERR
" Mapping '$new_letter' to known extension '$e'\n"
;
$canonical_form
=
$e
;
last
;
}
else
{
DEBUG > 1 and
print
STDERR
" Can't map '$new_letter' to unknown extension '$e'\n"
;
}
}
if
(
defined
$canonical_form
) {
$self
->{
'accept_codes'
}{
$new_letter
} =
$canonical_form
;
DEBUG > 2 and
print
"Extensor maps $new_letter => known element $canonical_form.\n"
;
}
else
{
$self
->{
'accept_codes'
}{
$new_letter
}
= (
@fallbacks
== 1) ?
$fallbacks
[0] : \
@fallbacks
;
DEBUG > 2 and
print
"Extensor maps $new_letter => fallbacks @fallbacks.\n"
;
}
}
else
{
DEBUG > 2 and
print
STDERR
"Extensor has bad syntax.\n"
;
$self
->whine(
$para
->[1]{
'start_line'
},
"Unknown =extend syntax: $content"
)
}
return
;
}
sub
_treat_Zs {
my
(
$self
,
@stack
) =
@_
;
my
(
$i
,
$treelet
);
my
$start_line
=
$stack
[0][1]{
'start_line'
};
while
(
$treelet
=
shift
@stack
) {
for
(
$i
= 2;
$i
<
@$treelet
; ++
$i
) {
next
unless
ref
$treelet
->[
$i
];
unless
(
$treelet
->[
$i
][0] eq
'Z'
) {
unshift
@stack
,
$treelet
->[
$i
];
next
;
}
DEBUG > 1 and
print
STDERR
"Nixing Z node @{$treelet->[$i]}\n"
;
unless
( @{
$treelet
->[
$i
]} == 2
or (@{
$treelet
->[
$i
]} == 3 and
$treelet
->[
$i
][2] eq
''
)
) {
$self
->whine(
$start_line
,
"A non-empty Z<>"
);
}
splice
(
@$treelet
,
$i
, 1);
--
$i
;
}
}
return
;
}
sub
_treat_Ls {
my
(
$self
,
@stack
) =
@_
;
my
(
$i
,
$treelet
);
my
$start_line
=
$stack
[0][1]{
'start_line'
};
while
(
$treelet
=
shift
@stack
) {
for
(
my
$i
= 2;
$i
<
@$treelet
; ++
$i
) {
next
unless
ref
$treelet
->[
$i
];
unless
(
$treelet
->[
$i
][0] eq
'L'
) {
unshift
@stack
,
$treelet
->[
$i
];
next
;
}
my
$ell
=
$treelet
->[
$i
];
DEBUG > 1 and
print
STDERR
"Ogling L node "
. pretty(
$ell
) .
"\n"
;
if
(@{
$ell
} == 3 and
$ell
->[2] =~ m!\A\s*/\s*\z!) {
$self
->whine(
$start_line
,
"L<> contains only '/'"
);
$treelet
->[
$i
] =
'L</>'
;
next
;
}
if
( @{
$ell
} == 2
or (@{
$ell
} == 3 and
$ell
->[2] eq
''
)
) {
$self
->whine(
$start_line
,
"An empty L<>"
);
$treelet
->[
$i
] =
'L<>'
;
next
;
}
if
( (!
ref
$ell
->[2] &&
$ell
->[2] =~ /\A\s/)
||(!
ref
$ell
->[-1] &&
$ell
->[-1] =~ /\s\z/)
) {
$self
->whine(
$start_line
,
"L<> starts or ends with whitespace"
);
}
if
(
my
(
$url_index
,
$text_part
,
$url_part
) =
sub
{
for
(2..
$#$ell
) {
next
if
ref
$ell
->[
$_
];
next
unless
$ell
->[
$_
] =~ m/^(?:([^|]*)\|)?(\w+:[^:\s]\S*)$/s;
return
(
$_
, $1, $2);
}
return
;
}->()
) {
$ell
->[1]{
'type'
} =
'url'
;
my
@text
= @{
$ell
}[2..
$url_index
-1];
push
@text
,
$text_part
if
defined
$text_part
;
my
@url
= @{
$ell
}[
$url_index
+1..
$#$ell
];
unshift
@url
,
$url_part
;
unless
(
@text
) {
$ell
->[1]{
'content-implicit'
} =
'yes'
;
@text
=
@url
;
}
$ell
->[1]{to} = Pod::Simple::LinkSection->new(
@url
== 1
?
$url
[0]
: [
''
, {},
@url
],
);
splice
@$ell
, 2,
$#$ell
,
@text
;
next
;
}
if
(@{
$ell
} == 3 and !
ref
$ell
->[2]) {
my
$it
=
$ell
->[2];
if
(
$it
=~ m{^[^/|]+[(][-a-zA-Z0-9]+[)]$}s) {
DEBUG > 1 and
print
STDERR
"Catching \"$it\" as manpage link.\n"
;
$ell
->[1]{
'type'
} =
'man'
;
$ell
->[1]{
'content-implicit'
} =
'yes'
;
$ell
->[1]{
'to'
} =
Pod::Simple::LinkSection->new(
$it
);
next
;
}
if
(
$it
=~ m/^[^\/\|,\$\%\@\ \"\<\>\:\
DEBUG > 1 and
print
STDERR
"Catching \"$it\" as ho-hum L<Modulename> link.\n"
;
$ell
->[1]{
'type'
} =
'pod'
;
$ell
->[1]{
'content-implicit'
} =
'yes'
;
$ell
->[1]{
'to'
} =
Pod::Simple::LinkSection->new(
$it
);
next
;
}
}
DEBUG > 1 and
print
STDERR
"Running a real parse on this non-trivial L\n"
;
my
$link_text
;
my
@ell_content
=
@$ell
;
splice
@ell_content
,0,2;
DEBUG > 3 and
print
STDERR
" Ell content to start: "
,
pretty(
@ell_content
),
"\n"
;
DEBUG > 3 and
print
STDERR
" Peering at L content for a '|' ...\n"
;
for
(
my
$j
= 0;
$j
<
@ell_content
; ++
$j
) {
next
if
ref
$ell_content
[
$j
];
DEBUG > 3 and
print
STDERR
" Peering at L-content text bit \"$ell_content[$j]\" for a '|'.\n"
;
if
(
$ell_content
[
$j
] =~ m/^([^\|]*)\|(.*)$/s) {
my
@link_text
= ($1);
$ell_content
[
$j
] = $2;
DEBUG > 3 and
print
STDERR
" FOUND a '|' in it. Splitting into [$1] + [$2]\n"
;
if
(
$link_text
[0] =~ m{[|/]}) {
$self
->whine(
$start_line
,
"alternative text '$link_text[0]' contains non-escaped | or /"
);
}
unshift
@link_text
,
splice
@ell_content
, 0,
$j
;
@ell_content
=
grep
ref
(
$_
)||
length
(
$_
),
@ell_content
;
$link_text
= [
grep
ref
(
$_
)||
length
(
$_
),
@link_text
];
DEBUG > 3 and
printf
" So link text is %s\n and remaining ell content is %s\n"
,
pretty(
$link_text
), pretty(
@ell_content
);
last
;
}
}
my
$section_name
;
DEBUG > 3 and
print
STDERR
" Peering at L-content for a '/' ...\n"
;
for
(
my
$j
= 0;
$j
<
@ell_content
; ++
$j
) {
next
if
ref
$ell_content
[
$j
];
DEBUG > 3 and
print
STDERR
" Peering at L-content text bit \"$ell_content[$j]\" for a '/'.\n"
;
if
(
$ell_content
[
$j
] =~ m/^([^\/]*)\/(.*)$/s) {
my
@section_name
= ($2);
$ell_content
[
$j
] = $1;
DEBUG > 3 and
print
STDERR
" FOUND a '/' in it."
,
" Splitting to page [...$1] + section [$2...]\n"
;
push
@section_name
,
splice
@ell_content
, 1+
$j
;
@ell_content
=
grep
ref
(
$_
)||
length
(
$_
),
@ell_content
;
@section_name
=
grep
ref
(
$_
)||
length
(
$_
),
@section_name
;
if
(
@section_name
and !
ref
(
$section_name
[0]) and !
ref
(
$section_name
[-1])
and
$section_name
[ 0] =~ m/^\"/s
and
$section_name
[-1] =~ m/\"$/s
and !(
@section_name
== 1 and
$section_name
[0] eq
'"'
)
) {
$section_name
[ 0] =~ s/^\"//s;
$section_name
[-1] =~ s/\"$//s;
DEBUG > 3 and
print
STDERR
" Quotes removed: "
, pretty(
@section_name
),
"\n"
;
}
else
{
DEBUG > 3 and
print
STDERR
" No need to remove quotes in "
, pretty(
@section_name
),
"\n"
;
}
$section_name
= \
@section_name
;
last
;
}
}
if
(!
$section_name
and
@ell_content
and !
ref
(
$ell_content
[0]) and !
ref
(
$ell_content
[-1])
and
$ell_content
[ 0] =~ m/^\"/s
and
$ell_content
[-1] =~ m/\"$/s
and !(
@ell_content
== 1 and
$ell_content
[0] eq
'"'
)
) {
$section_name
= [
splice
@ell_content
];
$section_name
->[ 0] =~ s/^\"//s;
$section_name
->[-1] =~ s/\"$//s;
$ell
->[1]{
'~tolerated'
} = 1;
}
if
(!
$section_name
and !
$link_text
and
@ell_content
and
grep
!
ref
(
$_
) && m/ /s,
@ell_content
) {
$section_name
= [
splice
@ell_content
];
$ell
->[1]{
'~deprecated'
} = 1;
}
unless
(
$link_text
) {
$ell
->[1]{
'content-implicit'
} =
'yes'
;
$link_text
= [];
push
@$link_text
,
'"'
,
@$section_name
,
'"'
if
$section_name
;
if
(
@ell_content
) {
$link_text
->[-1] .=
' in '
if
$section_name
;
push
@$link_text
,
@ell_content
;
}
}
if
(
@ell_content
== 1 and !
ref
(
$ell_content
[0])
and
$ell_content
[0] =~ m{^[^/]+[(][-a-zA-Z0-9]+[)]$}s
) {
$ell
->[1]{
'type'
} =
'man'
;
DEBUG > 3 and
print
STDERR
"Considering this ($ell_content[0]) a man link.\n"
;
}
else
{
$ell
->[1]{
'type'
} =
'pod'
;
DEBUG > 3 and
print
STDERR
"Considering this a pod link (not man or url).\n"
;
}
if
(
defined
$section_name
) {
$ell
->[1]{
'section'
} = Pod::Simple::LinkSection->new(
[
''
, {},
@$section_name
]
);
DEBUG > 3 and
print
STDERR
"L-section content: "
, pretty(
$ell
->[1]{
'section'
}),
"\n"
;
}
if
(
@ell_content
) {
$ell
->[1]{
'to'
} = Pod::Simple::LinkSection->new(
[
''
, {},
@ell_content
]
);
DEBUG > 3 and
print
STDERR
"L-to content: "
, pretty(
$ell
->[1]{
'to'
}),
"\n"
;
}
@$ell
= (
@$ell
[0,1],
defined
(
$link_text
) ?
splice
(
@$link_text
) :
''
);
DEBUG > 2 and
print
STDERR
"End of L-parsing for this node "
. pretty(
$treelet
->[
$i
]) .
"\n"
;
unshift
@stack
,
$treelet
->[
$i
];
}
}
return
;
}
sub
_treat_Es {
my
(
$self
,
@stack
) =
@_
;
my
(
$i
,
$treelet
,
$content
,
$replacer
,
$charnum
);
my
$start_line
=
$stack
[0][1]{
'start_line'
};
while
(
$treelet
=
shift
@stack
) {
for
(
my
$i
= 2;
$i
<
@$treelet
; ++
$i
) {
next
unless
ref
$treelet
->[
$i
];
if
(
$treelet
->[
$i
][0] eq
'L'
) {
my
$thing
;
foreach
my
$attrname
(
'section'
,
'to'
) {
if
(
defined
(
$thing
=
$treelet
->[
$i
][1]{
$attrname
}) and
ref
$thing
) {
unshift
@stack
,
$thing
;
DEBUG > 2 and
print
STDERR
" Enqueuing "
,
pretty(
$treelet
->[
$i
][1]{
$attrname
} ),
" as an attribute value to tweak.\n"
;
}
}
unshift
@stack
,
$treelet
->[
$i
];
next
;
}
elsif
(
$treelet
->[
$i
][0] ne
'E'
) {
unshift
@stack
,
$treelet
->[
$i
];
next
;
}
DEBUG > 1 and
print
STDERR
"Ogling E node "
, pretty(
$treelet
->[
$i
]),
"\n"
;
if
( @{
$treelet
->[
$i
]} == 2
or (@{
$treelet
->[
$i
]} == 3 and
$treelet
->[
$i
][2] eq
''
)
) {
$self
->whine(
$start_line
,
"An empty E<>"
);
$treelet
->[
$i
] =
'E<>'
;
next
;
}
unless
(@{
$treelet
->[
$i
]} == 3 and !
ref
(
$content
=
$treelet
->[
$i
][2])) {
$self
->whine(
$start_line
,
"An E<...> surrounding strange content"
);
$replacer
=
$treelet
->[
$i
];
splice
(
@$treelet
,
$i
, 1,
'E<'
,
splice
(
@$replacer
,2),
'>'
);
next
;
}
DEBUG > 1 and
print
STDERR
"Ogling E<$content>\n"
;
$charnum
= Pod::Escapes::e2charnum(
$content
);
DEBUG > 1 and
print
STDERR
" Considering E<$content> with char "
,
defined
(
$charnum
) ?
$charnum
:
"undef"
,
".\n"
;
if
(!
defined
(
$charnum
)) {
DEBUG > 1 and
print
STDERR
"I don't know how to deal with E<$content>.\n"
;
$self
->whine(
$start_line
,
"Unknown E content in E<$content>"
);
$replacer
=
"E<$content>"
;
}
elsif
(
$charnum
>= 255 and !UNICODE) {
$replacer
= ASCII ?
"\xA4"
:
"?"
;
DEBUG > 1 and
print
STDERR
"This Perl version can't handle "
,
"E<$content> (chr $charnum), so replacing with $replacer\n"
;
}
else
{
$replacer
= Pod::Escapes::e2char(
$content
);
DEBUG > 1 and
print
STDERR
" Replacing E<$content> with $replacer\n"
;
}
splice
(
@$treelet
,
$i
, 1,
$replacer
);
}
}
return
;
}
sub
_treat_Ss {
my
(
$self
,
$treelet
) =
@_
;
_change_S_to_nbsp(
$treelet
,0)
if
$self
->{
'nbsp_for_S'
};
return
;
}
sub
_change_S_to_nbsp {
my
(
$treelet
,
$in_s
) =
@_
;
my
$is_s
= (
'S'
eq
$treelet
->[0]);
$in_s
||=
$is_s
;
for
(
my
$i
= 2;
$i
<
@$treelet
; ++
$i
) {
if
(
ref
$treelet
->[
$i
]) {
if
( _change_S_to_nbsp(
$treelet
->[
$i
],
$in_s
) ) {
my
$to_pull_up
=
$treelet
->[
$i
];
splice
@$to_pull_up
,0,2;
splice
@$treelet
,
$i
, 1,
@$to_pull_up
;
$i
+=
@$to_pull_up
- 1;
}
}
else
{
$treelet
->[
$i
] =~ s/\s/
$Pod::Simple::nbsp
/g
if
$in_s
;
}
}
return
$is_s
;
}
sub
_accessorize {
no
strict
'refs'
;
foreach
my
$attrname
(
@_
) {
next
if
$attrname
=~ m/::/;
*{
caller
() .
'::'
.
$attrname
} =
sub
{
$Carp::CarpLevel
= 1, Carp::croak(
"Accessor usage: \$obj->$attrname() or \$obj->$attrname(\$new_value)"
)
unless
(
@_
== 1 or
@_
== 2) and
ref
$_
[0];
(
@_
== 1) ?
$_
[0]->{
$attrname
}
: (
$_
[0]->{
$attrname
} =
$_
[1]);
};
}
return
;
}
sub
filter {
my
(
$class
,
$source
) =
@_
;
my
$new
=
$class
->new;
$new
->output_fh(
*STDOUT
{IO});
if
(
ref
(
$source
||
''
) eq
'SCALAR'
) {
$new
->parse_string_document(
$$source
);
}
elsif
(
ref
(
$source
)) {
$new
->parse_file(
$source
);
}
else
{
$new
->parse_file(
$source
);
}
return
$new
;
}
sub
_out {
my
$class
=
shift
(
@_
);
my
$mutor
=
shift
(
@_
)
if
@_
and
ref
(
$_
[0] ||
''
) eq
'CODE'
;
DEBUG and
print
STDERR
"\n\n"
,
'#'
x 76,
"\nAbout to parse source: {{\n$_[0]\n}}\n\n"
;
my
$parser
=
ref
$class
&&
$class
->isa(__PACKAGE__) ?
$class
:
$class
->new;
$parser
->hide_line_numbers(1);
my
$out
=
''
;
$parser
->output_string( \
$out
);
DEBUG and
print
STDERR
" _out to "
, \
$out
,
"\n"
;
$mutor
->(
$parser
)
if
$mutor
;
$parser
->parse_string_document(
$_
[0] );
return
$out
;
}
sub
_duo {
my
$class
=
shift
(
@_
);
Carp::croak
"But $class->_duo is useful only in list context!"
unless
wantarray
;
my
$mutor
=
shift
(
@_
)
if
@_
and
ref
(
$_
[0] ||
''
) eq
'CODE'
;
Carp::croak
"But $class->_duo takes two parameters, not: @_"
unless
@_
== 2;
my
(
@out
);
while
(
@_
) {
my
$parser
=
$class
->new;
push
@out
,
''
;
$parser
->output_string( \(
$out
[-1] ) );
DEBUG and
print
STDERR
" _duo out to "
,
$parser
->output_string(),
" = $parser->{'output_string'}\n"
;
$parser
->hide_line_numbers(1);
$mutor
->(
$parser
)
if
$mutor
;
$parser
->parse_string_document(
shift
(
@_
) );
}
return
@out
;
}
1;