/usr/local/CPAN/Pod-MultiLang/Pod/MultiLang/Pod.pm
## ----------------------------------------------------------------------------
# Pod::MultiLang::Pod
# -----------------------------------------------------------------------------
# Mastering programed by YAMASHINA Hio
#
# Copyright 2003 YMIRLINK,Inc.
# -----------------------------------------------------------------------------
# $Id: /perl/Pod-MultiLang/lib/Pod/MultiLang/Pod.pm 624 2008-02-06T09:15:55.362158Z hio $
# -----------------------------------------------------------------------------
package Pod::MultiLang::Pod;
use strict;
use File::Spec::Functions;
use Hash::Util qw(lock_keys);
use Cwd;
use UNIVERSAL qw(isa can);
use List::Util qw(first);
use Pod::ParseLink qw(parselink);
use Pod::MultiLang;
use Pod::MultiLang::Dict;
our @ISA = qw(Pod::MultiLang Pod::Parser);
use constant
{
PARA_VERBATIM => 1,
PARA_TEXTBLOCK => 2,
PARA_HEAD => 3,
PARA_OVER => 4,
PARA_BACK => 5,
PARA_ITEM => 6,
PARA_BEGIN => 7,
PARA_END => 8,
PARA_FOR => 9,
PARA_ENCODING => 10,
PARA_POD => 11,
PARA_CUT => 12,
};
use constant
{
PARAINFO_TYPE => 0,
PARAINFO_PARAOBJ => 1,
# =head
PARAINFO_CONTENT => 2,
PARAINFO_ID => 3,
PARAINFO_HEADSIZE => 4,
# =over,item,back
PARAINFO_LISTTYPE => 2,
#PARAINFO_ID => 3,
};
use constant
{
DEFAULT_LANG => 'en',
};
use constant
{
VERBOSE_NONE => 0,
VERBOSE_ERROR => 10,
VERBOSE_NOLINK => 20,
VERBOSE_WARN => 30,
VERBOSE_DEFAULT => 50,
VERBOSE_FINDLINK => 90,
VERBOSE_VERBOSE => 80,
VERBOSE_DEBUG => 95,
VERBOSE_FULL => 100,
};
sub verbmsg
{
my ($parser,$level) = @_;
if( $parser->{_verbose}>=$level )
{
my $verbout = $parser->{_verbout};
print $verbout @_[2..$#_];
}
}
# -----------------------------------------------------------------------------
# $parser->_map_head_word($ptree)
# head ã®ããã¹ãã«åºæ¬è¨³ãä»ãã
#
sub _map_head_word
{
my ($parser,$ptree) = @_;
ref($ptree) or $ptree = Pod::Paragraph->new(-text=>$ptree);
my $text = $ptree->text();
$text =~ s/^\s+//;
$text =~ s/\s+$//;
my @text = Pod::MultiLang::Dict->find_word($parser->{langs},$text);
my $num_found = grep{defined($_)}@text;
if( $num_found==0 )
{
return $ptree;
}
if( $num_found==1 )
{
my $i = 0;
foreach(@text)
{
if( defined($_) && $parser->{langs}[$i] && $parser->{langs}[$i]eq'en' )
{
# default only.
return $ptree;
}
++$i;
}
}
my $i=0;
my $result = $text;
foreach(@text)
{
if( defined($_) )
{
$result .= "\nJ<$parser->{langs}[$i];$_>";
}
++$i;
}
$ptree->text($result);
$ptree;
}
# -----------------------------------------------------------------------------
# new
# ã³ã³ã¹ãã©ã¯ã¿
#
sub new
{
my $pkg = shift;
ref($pkg) and $pkg = ref($pkg);
my %arg = @_&&ref($_[0])eq'HASH'?%{$_[0]}:@_;
# SUPER ã¯ã©ã¹ã使ã£ã¦ã¤ã³ã¹ã¿ã³ã¹ãçæ.
#
my @passarg = map{exists($arg{$_})?($_=>$arg{$_}):()}qw(langs);
my $parser = $pkg->SUPER::new(@passarg);
# è¦åºãå¤æè¾æ¸ã®ãã¼ã
#
exists($arg{langs}) and Pod::MultiLang::Dict->load_dict($arg{langs});
# è¨å®ãè¨é²
#
$parser->{opt_use_index} = 1;
$parser->{opt_default_lang} = $arg{default_lang} || DEFAULT_LANG;
$parser->{_in_charset} = $arg{in_charset} || 'utf-8';
$parser->{_out_charset} = $arg{out_charset} || 'utf-8';
$parser->{_langstack} = undef;
$parser->{linkcache} = {};
@$parser{qw(_verbose _verbout
langs _expandlangs _default_lang _fetchlangs
_langstack _neststack _skipblock _iseqstack
paras heads items
_cssprefix
out_outfile out_outdir out_topdir
)} = ();
@$parser{qw( _INFILE _OUTFILE _PARSEOPTS _CUTTING
_INPUT _OUTPUT _CALLBACKS _TOP_STREAM _ERRORSUB
_INPUT_STREAMS
)} = ();
#_SELECTED_SECTIONS
#lock_keys(%$parser);
$parser;
}
# -----------------------------------------------------------------------------
# begin_pod
# åæå
#
sub begin_pod
{
my $parser = shift;
$parser->SUPER::begin_pod(@_);
$parser->{_verbose} = VERBOSE_DEFAULT;
$parser->{_verbout} = \*STDERR;
$parser->{_expandlangs} = undef;
$parser->{_default_lang} = $parser->{opt_default_lang};
$parser->{_fetchlangs} = undef;
$parser->{_langstack} = [undef];
my $outfile = $parser->output_file();
file_name_is_absolute($outfile) or $outfile = File::Spec->rel2abs($outfile);
my $outdir = (File::Spec->splitpath($outfile))[1];
$parser->{out_outfile} = $outfile;
$parser->{out_outdir} = $outdir;
$parser->{out_topdir} = File::Spec->abs2rel(cwd(),$outdir)||'';
# ãã£ã¬ã¯ããªã¯æ«å°¾/ä»ãã«æ£è¦å
foreach(@$parser{qw(out_topdir out_outdir)})
{
defined($_) && !m/\/$/ and $_.='/';
}
if( $parser->{_verbose}>=VERBOSE_FULL )
{
my $out = $$parser{_verbout};
print $out $parser->input_file()."\n";
print $out "scan...\n";
}
}
# -----------------------------------------------------------------------------
# interior_sequence
# è£
飾符å·ã®å±é
#
sub interior_sequence
{
my ($parser, $seq_command, $seq_argument,$seq_obj) = @_;
my $ldelim = $seq_obj->left_delimiter();
my $rdelim = $seq_obj->right_delimiter();
if( $seq_command eq 'I' )
{
}elsif( $seq_command eq 'B' )
{
}elsif( $seq_command eq 'C' )
{
}elsif( $seq_command eq 'L' )
{
die "L<> not processed here..";
}elsif( $seq_command eq 'E' )
{
}elsif( $seq_command eq 'F' )
{
}elsif( $seq_command eq 'S' )
{
}elsif( $seq_command eq 'X' )
{
}elsif( $seq_command eq 'Z' )
{
}elsif( $seq_command eq 'J' )
{
die "J<> not processed here..";
}
return "$seq_command$ldelim$seq_argument$rdelim";
}
# -----------------------------------------------------------------------------
# buildtext
# paraobj ãã text ãçæ
#
sub buildtext
{
my ($parser,$paraobj) = @_;
my $ptree;
if( isa($paraobj,'Pod::Paragraph') )
{
$ptree = $parser->parse_text($paraobj->text(),($paraobj->file_line())[1]);
}else
{
$ptree = $paraobj;
}
# @list containts [langs..,,no-lang];
my @list = $parser->parse_mlpod($ptree);
my @text;
for( my $i=0; $i<=$#{$parser->{langs}}; ++$i )
{
my $lang = $parser->{langs}[$i];
if( defined($list[$i]) )
{
# has text for this language.
#
push(@text,$list[$i]);
}elsif( $parser->{langs}[$i] eq $parser->{_default_lang} )
{
# no text for this language, but this is original language.
#
unshift(@text,$list[-1]);
}
}
my $ret = join('',map{s/\n(\s*\n)+/\n/g;"$_\n"}grep{/\S/}@text);
if( $ret eq '' )
{
$ret = (grep{defined($_)&&/\S/} @list[-1,0..$#list-1],'Z<>')[0];
}
$ret;
}
sub _a2s{ join('-',map{defined($_)?"[$_]":'{undef}'}@_) }
# -----------------------------------------------------------------------------
# $idx = $parser->_find_lang_index($lang);
# if not found, returns undef.
#
sub _find_lang_index
{
my ($this,$lang) = @_;
for( my $i=0; $i<=$#{$this->{langs}}; ++$i )
{
if( $this->{langs}[$i] eq $lang )
{
return $i;
}
}
undef;
}
# -----------------------------------------------------------------------------
# $ret = $parser->on_mlpod_plain($text);
#
sub on_mlpod_plain
{
my $parser = shift;
my $text = shift;
$text;
}
# -----------------------------------------------------------------------------
# $ret = $parser->on_mlpod_link($parselink,$seq_obj);
#
sub on_mlpod_link
{
my $parser = shift;
my $parselink = shift;
my $seq_obj = shift;
my ($text, $inferred, $name, $section, $type) = @$parselink;
my $seq_command = 'L';
my $seq_argument = "";
defined($text) and $seq_argument .= "$text|";
defined($name) and $seq_argument .= "$name";
defined($section) and $seq_argument .= "/$section";
my $ldelim = $seq_obj->left_delimiter();
my $rdelim = $seq_obj->right_delimiter();
return "$seq_command$ldelim$seq_argument$rdelim";
}
# -----------------------------------------------------------------------------
# @ret = $parser->parse_mlpod($ptree,$inlang);
# Pod::Parser, InteriorSequence çã®å¦ç.
# @ret: è¨èªæ¯ã®å¤æçµæ.
#
sub parse_mlpod
{
my ($parser,$ptree,$inlang) = @_;
# @ret[0..$#langs]: for that lang.
# $ret[-1], $ret[@langs]: fallback.
my @ret = ((undef)x@{$parser->{langs}},'');
# find index for default lang.
my $idx_default_lang = $parser->_find_lang_index($parser->{_default_lang})||0;
if( can($ptree,'parse_tree') )
{
$ptree = $ptree->parse_tree();
}
my @children = can($ptree,'children')?$ptree->children():isa($ptree,'ARRAY')?@$ptree:die "unknown object : $ptree";
#print STDERR "in: @{[scalar@children]} ",_a2s(@children),"\n";
foreach (@children)
{
if( !ref($_) )
{
# plain text.
$ret[-1] .= $parser->on_mlpod_plain($_);
next;
}
my $cmd_name = $_->cmd_name();
if( $cmd_name ne 'J' && $cmd_name ne 'L' )
{
# normal iseq.
#print STDERR "normal iseq\n";
# iseq ã®ä¸èº«ã mlpod åè§£.
#
my @child = $parser->parse_mlpod($_->parse_tree());
#print STDERR" child : $#child "._a2s(@child)."\n";
# default_lang ãæªå®ç¾©ã ã£ãã, è¨èªæå®ãªãé¨åãå
ã¦ã.
#
if( !defined($child[$idx_default_lang])
&& grep{defined($_)} @child[0..$#{$parser->{langs}}] )
{
$child[$idx_default_lang] = $child[-1];
}
# è£
飾符å·ã®å±é.
#
my $cmd_name = $_->cmd_name();
for( my $i=0; $i<=$#child; ++$i )
{
if( defined($child[$i]) )
{
my $ret = $parser->interior_sequence($cmd_name,$child[$i],$_);
defined($ret[$i]) or $ret[$i] = '';
$ret[$i] .= $ret;
}
}
}elsif( $cmd_name eq 'L' )
{
# link iseq.
#print STDERR "link iseq\n";
#
my $content = $_->raw_text();
$content =~ s/^L\<+\s*//;
$content =~ s/\s*\>+$//;
my ($text, $inferred, $name, $section, $type) = parselink($content);
if( !$section && $name =~ / / )
{
$section = $name;
$name = '';
}
if( $content !~ /J\</ )
{
# if there is no J<> sequences.
my $parselink = [$text,$inferred,$name,$section,$type];
my $link = $parser->on_mlpod_link($parselink,$_);
defined($ret[-1]) or $ret[-1] = '';
$ret[-1] .= $link;
next;
}
my $line = ($_->file_line())[1];
foreach($text, $name, $section)
{
if( !defined($_) )
{
$_ = [(undef)x$#ret];
next;
}
my $ptree = $parser->parse_text($_,$line);
my @child = $parser->parse_mlpod($ptree);
# default_lang ãæªå®ç¾©ã ã£ãã, è¨èªæå®ãªãé¨åãå
ã¦ã.
# (å
¨é¨æªå®ç¾©ãªãå¿
è¦ãªã)
if( defined($idx_default_lang)
&& !defined($child[$idx_default_lang])
&& grep{defined($_)}@child[0..$#{$parser->{langs}}] )
{
$child[$idx_default_lang] = $child[-1];
}
foreach(grep{defined($_)}@child)
{
s/^\s+//;
s/\s+$//;
}
$_ = \@child;
}
# è£
飾符å·ã®å±é.
# expand interior sequences.
#
my $cmd_name = $_->cmd_name();
my $lang = $parser->{_langstack}[-1]||$parser->{_default_lang};
my $idx = $parser->_find_lang_index($lang);
defined($idx) or $idx = $idx_default_lang;
my $select_proper_text = sub{
my $text1 = shift;
my $text2 = shift;
if( defined($text1) && $text1 ne '' )
{
$text1;
}elsif( defined($text2) && $text2 ne '' )
{
$text2;
}elsif( defined($text1) || defined($text2) )
{
'';
}else
{
undef;
}
};
my $text_lang = $text->[$idx];
my $text_def = $text->[$idx_default_lang];
my $text_sel = $select_proper_text->($text_lang, $text_def);
my $name_lang = $name->[$idx];
my $name_def = $name->[$idx_default_lang];
my $name_sel = $select_proper_text->($name_lang, $name_def);
my $section_lang = $section->[$idx];
my $section_def = $$section[$idx_default_lang];
my $section_sel = $select_proper_text->($section_lang, $section_def);
my $parselink = [$text_sel,$inferred,$name_sel,$section_sel,$type];
my $link = $parser->on_mlpod_link($parselink,$_);
defined($ret[-1]) or $ret[-1] = '';
$ret[-1] .= $link;
# if cmd_name eq 'L'
}else
{
# lang iseq.
#
my $iseq = $_;
my $first = ($iseq->parse_tree()->children())[0] || '';
push(@{$parser->{_langstack}},$first=~/^\s*(\w+)\s*[\/;]/?$1:$parser->{_langstack}[-1]);
my @child = $parser->parse_mlpod($iseq->parse_tree());
pop(@{$parser->{_langstack}});
$child[-1] =~ s,^\s*(\w+)\s*[/;]\s*,,;
my $lang = $1;
if( !defined($lang) )
{
$parser->verbmsg(VERBOSE_ERROR,"no lang in J<>, use default-lang [$parser->{_default_lang}] at ".$iseq->file_line()."\n");
$lang = $parser->{_default_lang};
}
for( my $i=0; $i<=$#{$parser->{langs}}; ++$i )
{
$parser->{langs}[$i] ne $lang and next;
$ret[$i] .= $child[-1];
last;
}
#print STDERR " iseq: $#ret ",_a2s(@ret),"\n";
}
}
$ret[-1]=~/\S/ or $ret[-1]='';
#print "out: @{[scalar@ret]} ",_a2s(@ret),"\n";
@ret;
}
# -----------------------------------------------------------------------------
# end_pod
# at end of parsing pod.
# build html and output it.
#
sub end_pod
{
my $parser = shift;
my ($command, $paragraph, $line_num) = @_;
$parser->SUPER::end_pod(@_);
if( !@{$parser->{paras}} )
{
warn "input has no paragraphs";
}
$parser->rebuild();
$parser->output_pod();
}
# -----------------------------------------------------------------------------
# rebuild
# build infomations needed for html.
#
sub rebuild
{
my ($parser, $command, $paragraph, $line_num) = @_;
if( $parser->{_verbose}>=VERBOSE_FULL )
{
my $out = $$parser{_verbout};
print $out "scan done, rebuild...\n";
}
# build indices from "head"s.
#
foreach (@{$parser->{heads}})
{
my ($paraobj) = $$_[PARAINFO_PARAOBJ];
if( $paraobj->text() !~ /[^\w\s&]/ )
{
$paraobj = $parser->_map_head_word($paraobj);
$_->[PARAINFO_PARAOBJ] = $paraobj;
}
$_->[PARAINFO_CONTENT] = $parser->buildtext($paraobj);
$_->[PARAINFO_HEADSIZE] = ($paraobj->cmd_name()=~/(\d)/)[0]||0;
$_->[PARAINFO_ID] = q/id is not used/;
}
# build indices from "item"s too.
#
foreach (@{$parser->{items}})
{
my ($paraobj,$listtype) = @$_[PARAINFO_PARAOBJ,PARAINFO_LISTTYPE];
$listtype ne 'dl' and next;
if( $paraobj->text() !~ /[^\w\s&]/ )
{
$paraobj = $parser->_map_head_word($paraobj);
$_->[PARAINFO_PARAOBJ] = $paraobj;
}
$_->[PARAINFO_ID] = q/id is not used/;
}
}
# -----------------------------------------------------------------------------
# output_pod
# podãåºå
#
sub output_pod
{
my ($parser, $command, $paragraph, $line_num) = @_;
my $out_fh = $parser->output_handle();
if( $parser->{_verbose}>=VERBOSE_FULL )
{
$parser->vermbsg(VERBOSE_FULL,"ok, output...\n");
}
#binmode($out_fh,":encoding($parser->{_out_charset})");
#print defined($out_fh)?"[$out_fh]\n":"{undef}\n";
binmode($out_fh,":bytes");
# åºåéå§
#
# æ¬æã®åºå.
#
my $in_item = 0;
my $first_item = 1;
my @verbpack;
my @blockstack;
use constant {STK_PARAOBJ=>0,STK_BEHAVIOR=>1,};
use constant {BHV_NONE=>'none',BHV_NORMAL=>'normal',BHV_VERBATIM=>'verbatim',BHV_IGNORE=>'ignore'};
foreach (@{$parser->{paras}})
{
my ($paratype,$paraobj) = @$_[PARAINFO_TYPE,PARAINFO_PARAOBJ];
$parser->{_iseqstack} = [];
# ignore ç¶æ
ã®ç¢ºèª
#
if( grep{$_->[STK_BEHAVIOR]eq BHV_IGNORE}@blockstack )
{
#print $out_fh " in ignore ...\n";
if( $paratype==PARA_END
&& $_->[PARAINFO_CONTENT] eq $blockstack[-1]->[STK_PARAOBJ][PARAINFO_CONTENT] )
{
my $fin = pop(@blockstack);
my $mode = $_->[PARAINFO_CONTENT];
my $outtext = "";
print $out_fh $parser->_from_to($outtext);
}
next;
}
# é£ç¶ãã verbose ã®é£çµå¦ç.
#
my $blk = first{(ref($_)||'')eq'ARRAY'&&$$_[STK_BEHAVIOR]ne BHV_IGNORE}reverse @blockstack;
if( $paratype==PARA_VERBATIM || ($paratype!=PARA_END&&$blk&&$blk->[STK_BEHAVIOR]eq BHV_VERBATIM) )
{
my $text = $paraobj->text();
$text = $parser->_from_to($text);
$text !~ /^\n*$/ and push(@verbpack,$text);
next;
}elsif( @verbpack )
{
my $text = join('',@verbpack);
$text =~ s/\s*$//;
if( $text !~ /^\n*$/ )
{
$text =~ s/\n+$/\n/;
my $outtext = "$text\n\n";
$outtext = $parser->_from_to($outtext);
print $out_fh $outtext;
}
@verbpack = ();
}
# æ®éã«åºåå¦ç.
# $outtext ã«ã¯ _from_to æ¸ã¿ã®ããã¹ãã追å .
#
my $outtext;
if( $paratype==PARA_TEXTBLOCK )
{
my $text = $parser->buildtext($paraobj);
$text = $parser->_from_to($text);
$text =~ /^\s*$/ and next;
$outtext = $text."\n";
}elsif( $paratype==PARA_HEAD )
{
my $text = $_->[PARAINFO_CONTENT];
my $cmd = $paraobj->cmd_name();
$text = $parser->_from_to($text);
$text =~ s/\n(\s*\n)+/\n/g;
$outtext = "=$cmd $text\n";
}elsif( $paratype==PARA_OVER )
{
$outtext = $paraobj->raw_text();
my ($type) = $_->[PARAINFO_LISTTYPE];
$first_item = 1;
my $stk = [];
$stk->[STK_PARAOBJ] = $_;
$stk->[STK_BEHAVIOR] = BHV_NORMAL;
push(@blockstack,$stk);
}elsif( $paratype==PARA_BACK )
{
$outtext = '';
if( $in_item )
{
--$in_item;
}
pop(@blockstack);
$outtext .= "=back\n\n";
}elsif( $paratype==PARA_ITEM )
{
my ($type,$id) = @$_[PARAINFO_LISTTYPE,PARAINFO_ID];
$outtext = '';
if( !@blockstack )
{
my $stk = [];
$stk->[STK_PARAOBJ] = $type;
$stk->[STK_BEHAVIOR] = BHV_NORMAL;
push(@blockstack,$stk);
$outtext .= "=over\n\n";
}
if( $type eq 'ul' || $type eq 'ol' )
{
$outtext .= "=item ".$parser->buildtext($paraobj)."\n\n";
}elsif( $type eq 'dl' )
{
my $item = $parser->buildtext($paraobj);
$item =~ s/^\s+//;
$item =~ s/\s+$//;
$item = $parser->_from_to($item);
$outtext .= "=item $item\n\n";
}else
{
$parser->vermsg(VERBOSE_ERROR,"unknown list type [$type]");
}
$first_item and undef($first_item),++$in_item;
}elsif( $paratype==PARA_BEGIN || $paratype==PARA_END
|| $paratype==PARA_FOR || $paratype==PARA_ENCODING
|| $paratype==PARA_POD || $paratype==PARA_CUT )
{
my $text = $_->[PARAINFO_CONTENT];
my $cmd = $paraobj->cmd_name();
if( $text ne '' )
{
$text = $parser->_from_to($text);
$text =~ s/\n(\s*\n)+/\n/g;
$outtext = "=$cmd $text\n\n";
}else
{
$outtext = "=$cmd\n\n";
}
}else
{
$parser->verbmsg(VERBOSE_ERROR,"what\'s got?? [$paratype]");
next;
}
if( defined($outtext) )
{
# $outtext 㯠_from_to æ¸ã¿.
print $out_fh $outtext;
}
}
if( @verbpack )
{
my $text = join('',@verbpack);
if( $text !~ /^\n*$/ )
{
my $outtext = "$text\n\n";
$outtext = $parser->_from_to($outtext);
print $out_fh $outtext;
}
}
# output done.
}
# =============================================================================
# ã¦ã¼ãã£ãªãã£é¢æ°ç¾¤
# =============================================================================
1;
__END__
# -----------------------------------------------------------------------------
# End of File.
# -----------------------------------------------------------------------------