Parent Directory
|
Revision Log
do_index: Removed.
| 1 | wakaba | 1.9 | #!/usr/bin/perl |
| 2 | wakaba | 1.16 | # wiki.cgi - This is YukiWiki, yet another Wiki clone. |
| 3 | wakaba | 1.9 | # |
| 4 | wakaba | 1.16 | # This program is free software; you can redistribute it and/or |
| 5 | # modify it under the same terms as Perl itself. | ||
| 6 | |||
| 7 | use strict; | ||
| 8 | w | 1.44 | use lib qw(./lib); |
| 9 | wakaba | 1.16 | use CGI::Carp qw(fatalsToBrowser); |
| 10 | w | 1.46 | our $VERSION = do{my @r=(q$Revision: 1.26 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r}; |
| 11 | w | 1.43 | |
| 12 | require 'wikidata/suikawiki-config.ph'; | ||
| 13 | wakaba | 1.16 | use Fcntl; |
| 14 | ############################## | ||
| 15 | wakaba | 1.31 | my %fmt; ## formatter objects |
| 16 | wakaba | 1.22 | my %embed_command = ( |
| 17 | searched => '^\[\[#searched:([^\]]+)\]\]$', | ||
| 18 | w | 1.43 | form => qr/\[\[\#form(?:\(([A-Za-z0-9-]+)\))?:'((?:[^'\\]|\\.)*)':'((?:[^'\\]|\\.)*)'(?::'((?:[^'\\]|\\.)*)')?\]\]/, |
| 19 | wakaba | 1.22 | ); |
| 20 | w | 1.43 | our ($modifier_dbtype,$url_cgi,%uri,%PathTo,$use_exists); |
| 21 | w | 1.45 | our (%PageName,$kanjicode,$lang,%FixedPage); |
| 22 | w | 1.43 | |
| 23 | wakaba | 1.16 | ############################## |
| 24 | my $info_LastModified = 'LastModified'; | ||
| 25 | my $info_IsFrozen = 'IsFrozen'; | ||
| 26 | ############################## | ||
| 27 | my %form; | ||
| 28 | w | 1.46 | our %database; |
| 29 | our $database; | ||
| 30 | wakaba | 1.16 | my %infobase; |
| 31 | my %interwiki; | ||
| 32 | ############################## | ||
| 33 | my %page_command = ( | ||
| 34 | w | 1.43 | $PageName{RssPage} => 'rss', |
| 35 | wakaba | 1.16 | ); |
| 36 | my %command_do = ( | ||
| 37 | read => \&do_read, | ||
| 38 | wakaba | 1.25 | TEXT_CSS => \&do_output_css, |
| 39 | wakaba | 1.16 | edit => \&do_edit, |
| 40 | adminedit => \&do_adminedit, | ||
| 41 | adminchangepassword => \&do_adminchangepassword, | ||
| 42 | write => \&do_write, | ||
| 43 | searchform => \&do_searchform, | ||
| 44 | comment => \&do_comment, | ||
| 45 | wakaba | 1.28 | RandomJump => \&do_random_jump, |
| 46 | wakaba | 1.16 | rss => \&do_rss, |
| 47 | diff => \&do_diff, | ||
| 48 | wakaba | 1.41 | wikiform => \&do_wikiform, |
| 49 | map => \&do_map, | ||
| 50 | wakaba | 1.16 | ); |
| 51 | wakaba | 1.33 | my $UA = ''; ## User agent name |
| 52 | wakaba | 1.41 | $| = 1; |
| 53 | wakaba | 1.16 | ############################## |
| 54 | wakaba | 1.9 | |
| 55 | sub main { | ||
| 56 | wakaba | 1.32 | $UA = $main::ENV{HTTP_USER_AGENT}; |
| 57 | wakaba | 1.16 | &open_db; |
| 58 | &init_form; | ||
| 59 | if ($command_do{$form{mycmd}}) { | ||
| 60 | &{$command_do{$form{mycmd}}}; | ||
| 61 | wakaba | 1.9 | } else { |
| 62 | wakaba | 1.42 | &{$command_do{read}}; |
| 63 | wakaba | 1.9 | } |
| 64 | wakaba | 1.16 | &close_db; |
| 65 | wakaba | 1.9 | } |
| 66 | |||
| 67 | sub do_read { | ||
| 68 | wakaba | 1.26 | my $content = $database{$form{mypage}}; |
| 69 | wakaba | 1.41 | #print "content-type:text/plain;charset=euc-jp\n\n".gmtime."Get Lastmodified\n"; |
| 70 | wakaba | 1.27 | my $lm = &get_info($form{mypage}, $info_LastModified); |
| 71 | wiki::referer::add ($form{mypage}, $ENV{HTTP_REFERER}); | ||
| 72 | wakaba | 1.32 | wiki::useragent::add ($ENV{HTTP_USER_AGENT}); |
| 73 | wakaba | 1.41 | #print gmtime."Search...\n"; |
| 74 | wakaba | 1.29 | my ($r, $c) = get_search_result ($form{mypage}); |
| 75 | my $rl = wiki::referer::list_html ($form{mypage}); | ||
| 76 | my @toc; | ||
| 77 | wakaba | 1.41 | push @toc, qq(-<a href="#wikipage-see-also">@{[&Resource('SeeAlso',escape=>1)]}</a>) if $c; |
| 78 | push @toc, qq(-<a href="#wikipage-referer">@{[&Resource('Referers',escape=>1)]}</a>) if $rl; | ||
| 79 | wakaba | 1.26 | my $cf = 'SuikaWiki/0.9'; |
| 80 | wakaba | 1.27 | ## Should be support at least: |
| 81 | ## - 'SuikaWiki/0.9' CRLF | ||
| 82 | ## - 'H2H/' ("0.9" / "1.0" / "1.1") CRLF | ||
| 83 | ## - "/*" WSP* 'W3C-CSS/' ("1.0" / "2.0") "*/" CRLF | ||
| 84 | wakaba | 1.32 | $cf = $1 if $content =~ s#^(?:/\*\s*|[\#<]\?)?([A-Z][A-Za-z0-9-]+/[0-9.]+(?:[^0-9.\x0D\x0A][^\x0D\x0A]*)?)[\x0D\x0A]+##s; |
| 85 | wakaba | 1.28 | if ($cf =~ m!^(?:\#\?)?SuikaWiki/0.9(?:$|\s)!) { |
| 86 | wakaba | 1.41 | #print gmtime."Header...\n"; |
| 87 | wakaba | 1.42 | &print_header ($form{mypage}, -last_modified => $lm, -expires => time + 120, |
| 88 | -content_format => $cf, -noindex => ($cf =~ /obsoleted="yes"/ ? 1 : 0)); | ||
| 89 | wakaba | 1.41 | #print "\n". gmtime."Body...\n"; |
| 90 | wakaba | 1.29 | &print_content ($content, content_format => $cf, last_modified => $lm, |
| 91 | -toc => \@toc); | ||
| 92 | w | 1.45 | print &text_to_html (q([[#comment]])) if $cf !~ /obsoleted="yes"/ && !$FixedPage{$form{mypage}}; |
| 93 | wakaba | 1.26 | } else { |
| 94 | wakaba | 1.42 | &print_header($form{mypage}, -expires => time + 120, -last_modified => $lm); |
| 95 | wakaba | 1.26 | print "<pre>@{[&escape($content)]}</pre>"; |
| 96 | } | ||
| 97 | wakaba | 1.22 | if ($c) { |
| 98 | wakaba | 1.41 | print qq{<h2 @{[&id_and_name('wikipage-see-also')]}>@{[&Resource('SeeAlso',escape=>1)]}</h2>}; |
| 99 | wakaba | 1.22 | print $r; |
| 100 | } | ||
| 101 | wakaba | 1.29 | if ($rl) { |
| 102 | wakaba | 1.41 | print qq(<div @{[&id_and_name('wikipage-referer')]}><h2>@{[&Resource('Referers',escape=>1)]}</h2>\n$rl</div>\n); |
| 103 | wakaba | 1.27 | } |
| 104 | wakaba | 1.41 | #print "\n". gmtime."Footer...\n"; |
| 105 | wakaba | 1.27 | &print_footer($form{mypage}, $lm); |
| 106 | wakaba | 1.41 | #print "\n". gmtime."Fin...\n"; |
| 107 | wakaba | 1.9 | } |
| 108 | |||
| 109 | wakaba | 1.25 | sub do_output_css { |
| 110 | wakaba | 1.37 | wiki::referer::add ($form{mypage}, $ENV{HTTP_REFERER}); |
| 111 | wiki::useragent::add ($ENV{HTTP_USER_AGENT}); | ||
| 112 | wakaba | 1.25 | my $content = $database{$form{mypage}}; |
| 113 | if ($content =~ m#^\s*/\*\s*W3C-CSS#) { | ||
| 114 | wakaba | 1.41 | my $lm = gmtime &get_info($form{mypage}, $info_LastModified); |
| 115 | print "Content-Type: text/css; charset=@{[&get_charset_name($kanjicode)]}\n"; | ||
| 116 | wakaba | 1.29 | print "Last-Modified: $lm\n"; |
| 117 | wakaba | 1.42 | print "Expires: @{[scalar gmtime time+3600]}\n"; ## TODO: don't use asctime |
| 118 | wakaba | 1.25 | print "\n"; |
| 119 | print $content; | ||
| 120 | } else { | ||
| 121 | print "Status: 406 Unsupported Media Type\n"; | ||
| 122 | wakaba | 1.29 | &print_header('WikiPageIsNotCSS', -noindex => 1); |
| 123 | wakaba | 1.25 | &print_content($database{WikiPageIsNotCSS}); |
| 124 | &print_footer('WikiPageIsNotCSS'); | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | wakaba | 1.32 | sub id_and_name ($) { |
| 129 | my $name = shift; | ||
| 130 | wakaba | 1.36 | if ($UA =~ m#Mozilla/[12]\.|Microsoft Internet Explorer#) { |
| 131 | wakaba | 1.32 | qq{id="$name"><a name="$name"></a}; |
| 132 | } else { | ||
| 133 | qq{id="$name"}; | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | wakaba | 1.9 | sub do_edit { |
| 138 | wakaba | 1.16 | my ($page) = &unarmor_name(&armor_name($form{mypage})); |
| 139 | if (not &is_editable($page)) { | ||
| 140 | w | 1.45 | &print_header($page, -noindex => 1, -expires => time+60); |
| 141 | wakaba | 1.41 | &print_message(&Resource('Error:ThisPageIsUneditable')); |
| 142 | wakaba | 1.16 | } elsif (&is_frozen($page)) { |
| 143 | w | 1.45 | &print_header($page, -noindex => 1, -expires => time+60); |
| 144 | wakaba | 1.41 | &print_message(&Resource('Error:ThisPageIsUneditable')); |
| 145 | wakaba | 1.16 | } else { |
| 146 | wakaba | 1.42 | &print_header($page, -noindex => 1, -expires => time+60); |
| 147 | wakaba | 1.16 | &print_editform($database{$page}, &get_info($page, $info_LastModified), admin=>0); |
| 148 | wakaba | 1.9 | } |
| 149 | wakaba | 1.30 | wiki::referer::add ($form{mypage}, $ENV{HTTP_REFERER}); |
| 150 | wakaba | 1.32 | wiki::useragent::add ($ENV{HTTP_USER_AGENT}); |
| 151 | wakaba | 1.30 | my ($r, $c) = get_search_result ($form{mypage}); |
| 152 | my $rl = wiki::referer::list_html ($form{mypage}); | ||
| 153 | if ($c) { | ||
| 154 | wakaba | 1.41 | print qq{<h2 id="wikipage-see-also">@{[&Resource('SeeAlso',escape=>1)]}</h2>}; |
| 155 | wakaba | 1.30 | print $r; |
| 156 | } | ||
| 157 | if ($rl) { | ||
| 158 | wakaba | 1.41 | print qq(<div id="wikipage-referer"><h2>@{[&Resource('Referers',escape=>1)]}</h2>\n$rl</div>\n); |
| 159 | wakaba | 1.30 | } |
| 160 | wakaba | 1.16 | &print_footer($page); |
| 161 | wakaba | 1.9 | } |
| 162 | |||
| 163 | wakaba | 1.16 | sub do_adminedit { |
| 164 | my ($page) = &unarmor_name(&armor_name($form{mypage})); | ||
| 165 | w | 1.45 | &print_header($page, -noindex => 1, -expires => time+60); |
| 166 | wakaba | 1.16 | if (not &is_editable($page)) { |
| 167 | wakaba | 1.41 | &print_message(&Resource('Error:ThisPageIsUneditable')); |
| 168 | wakaba | 1.9 | } else { |
| 169 | wakaba | 1.41 | &print_message(&Resource('Error:PasswordIsNotSpecified')); |
| 170 | wakaba | 1.16 | &print_editform($database{$page}, &get_info($page, $info_LastModified), admin=>1); |
| 171 | wakaba | 1.9 | } |
| 172 | wakaba | 1.16 | &print_footer($page); |
| 173 | wakaba | 1.9 | } |
| 174 | |||
| 175 | wakaba | 1.16 | sub do_adminchangepassword { |
| 176 | if ($form{mynewpassword} ne $form{mynewpassword2}) { | ||
| 177 | wakaba | 1.41 | &print_error(&Resource('Error:PasswordMismatch')); |
| 178 | wakaba | 1.16 | } |
| 179 | w | 1.43 | my ($validpassword_crypt) = &get_info($PageName{AdminSpecialPage}, 'AdminPassword'); |
| 180 | wakaba | 1.16 | if ($validpassword_crypt) { |
| 181 | if (not &valid_password($form{myoldpassword})) { | ||
| 182 | wakaba | 1.41 | &print_error(&Resource('Error:PasswordIsIncorrect')); |
| 183 | wakaba | 1.9 | } |
| 184 | wakaba | 1.16 | } |
| 185 | my ($sec, $min, $hour, $day, $mon, $year, $weekday) = localtime(time); | ||
| 186 | my (@token) = ('0'..'9', 'A'..'Z', 'a'..'z'); | ||
| 187 | my $salt1 = $token[(time | $$) % scalar(@token)]; | ||
| 188 | my $salt2 = $token[($sec + $min*60 + $hour*60*60) % scalar(@token)]; | ||
| 189 | my $crypted = crypt($form{mynewpassword}, "$salt1$salt2"); | ||
| 190 | w | 1.43 | &set_info($PageName{AdminSpecialPage}, 'AdminPassword', $crypted); |
| 191 | wakaba | 1.16 | |
| 192 | wakaba | 1.41 | &print_header('CompletedSuccessfully', -noindex => 1); |
| 193 | &print_message(&Resource('Error:PasswordIsChanged')); | ||
| 194 | &print_footer('CompletedSuccessfully'); | ||
| 195 | wakaba | 1.16 | } |
| 196 | |||
| 197 | w | 1.45 | sub valid_password ($) { |
| 198 | my ($validpassword_crypt) = &get_info($PageName{AdminSpecialPage}, 'AdminPassword'); | ||
| 199 | return crypt (shift, $validpassword_crypt) eq $validpassword_crypt ? 1 : 0; | ||
| 200 | } | ||
| 201 | |||
| 202 | wakaba | 1.16 | sub do_write { |
| 203 | if (&frozen_reject()) { | ||
| 204 | return; | ||
| 205 | wakaba | 1.9 | } |
| 206 | |||
| 207 | if (not &is_editable($form{mypage})) { | ||
| 208 | wakaba | 1.29 | &print_header($form{mypage}, -noindex => 1); |
| 209 | wakaba | 1.41 | &print_message(&Resource('Error:ThisPageIsUneditable')); |
| 210 | wakaba | 1.16 | &print_footer($form{mypage}); |
| 211 | wakaba | 1.9 | return; |
| 212 | } | ||
| 213 | |||
| 214 | wakaba | 1.16 | if (&conflict($form{mypage}, $form{mymsg})) { |
| 215 | wakaba | 1.9 | return; |
| 216 | } | ||
| 217 | |||
| 218 | if ($form{mymsg}) { | ||
| 219 | w | 1.46 | if ($form{mytouch} || !ref $database) { |
| 220 | $database{$form{mypage}} = $form{mymsg}; | ||
| 221 | } else { | ||
| 222 | $database->STORE ($form{mypage} => $form{mymsg}, -touch => 0); | ||
| 223 | wakaba | 1.16 | } |
| 224 | &set_info($form{mypage}, $info_IsFrozen, 0 + $form{myfrozen}); | ||
| 225 | wakaba | 1.36 | my $fragment = ''; |
| 226 | $fragment .= qq(;after_edit_cmd=@{[&encode($form{after_edit_cmd})]}) if $form{after_edit_cmd}; | ||
| 227 | wakaba | 1.33 | if ($form{__comment_anchor_index}) { |
| 228 | wakaba | 1.36 | $fragment .= qq(#anchor-$form{__comment_anchor_index}); |
| 229 | wakaba | 1.33 | } elsif ($form{__wikiform_anchor_index}) { |
| 230 | wakaba | 1.36 | $fragment .= qq(#wikiform-$form{__wikiform_anchor_index}); |
| 231 | wakaba | 1.33 | } |
| 232 | wakaba | 1.41 | &print_header('CompletedSuccessfully', -noindex => 1, -goto => $url_cgi.'?mycmd='.&encode($form{after_edit_cmd}||'read').';mypage='.&encode($form{mypage}).qq(;x-param=@{[time.[0..9]->[rand 10]]}$fragment)); |
| 233 | &print_message(&Resource('Error:SavedSuccessfully')); | ||
| 234 | &print_content(&Resource('Error:ContinueReading')." @{[&armor_name($form{mypage})]}"); | ||
| 235 | &print_footer('CompletedSuccessfully'); | ||
| 236 | wakaba | 1.16 | } else { |
| 237 | delete $database{$form{mypage}}; | ||
| 238 | delete $infobase{$form{mypage}}; | ||
| 239 | wakaba | 1.29 | &print_header($form{mypage}, -noindex => 1); |
| 240 | wakaba | 1.41 | &print_message(&Resource('Error:PageIsDeletedSuccessfully')); |
| 241 | wakaba | 1.16 | &print_footer($form{mypage}); |
| 242 | wakaba | 1.9 | } |
| 243 | } | ||
| 244 | |||
| 245 | wakaba | 1.22 | sub get_search_result ($;%) { |
| 246 | wakaba | 1.32 | my $word = lc shift; |
| 247 | wakaba | 1.22 | my %option = @_; |
| 248 | wakaba | 1.30 | my @r; |
| 249 | foreach my $page (keys %database) { | ||
| 250 | wakaba | 1.41 | next if !$option{-match_myself} && ($page eq $word); |
| 251 | my $content = lc $database{$page}; | ||
| 252 | wakaba | 1.32 | if (index (lc $page, $word) > -1) { |
| 253 | wakaba | 1.41 | my $c = $content =~ s/\Q$word\E//g; |
| 254 | wakaba | 1.30 | push @r, [$page, $c+20]; |
| 255 | wakaba | 1.32 | } elsif (index ($word, lc $page) > -1) { |
| 256 | wakaba | 1.41 | my $c = $content =~ s/\Q$word\E//g; |
| 257 | wakaba | 1.30 | push @r, [$page, $c+10]; |
| 258 | wakaba | 1.41 | } elsif (my $c = $content =~ s/\Q$word\E//g) { |
| 259 | wakaba | 1.30 | push @r, [$page, $c]; |
| 260 | wakaba | 1.22 | } |
| 261 | } | ||
| 262 | wakaba | 1.41 | #my $em = sub { my $s = shift; $s =~ s#(\Q$word\E)#<em>$1</em>#gi; $s }; |
| 263 | my $r = join "\n", map {qq(<li>[$_->[1]] <a href ="$url_cgi?@{[&encode($_->[0])]}" class="wiki">@{[&escape($_->[0])]}</a> <span class="wikipage-summary">@{[&escape(&get_subjectline($_->[0]))]}</span></li>)} sort {$b->[1] <=> $a->[1] || $a->[0] cmp $b->[0]} @r; | ||
| 264 | wakaba | 1.30 | $r = qq|<ul class="search-result">$r</ul>| if $r; |
| 265 | wantarray? ($r, scalar @r): $r; | ||
| 266 | wakaba | 1.22 | } |
| 267 | |||
| 268 | wakaba | 1.28 | sub do_random_jump { |
| 269 | my @list = keys %database; | ||
| 270 | my $name = &encode ($list[rand @list]); | ||
| 271 | wakaba | 1.41 | my $scheme = 'http'; |
| 272 | $scheme = lc $1 if $main::ENV{SERVER_PROTOCOL} =~ m#([A-Za-z0-9+.%-]+)#; | ||
| 273 | wakaba | 1.29 | print "Location: $scheme://$main::ENV{SERVER_NAME}:$main::ENV{SERVER_PORT}$url_cgi?$name\n"; |
| 274 | wakaba | 1.28 | print "\n"; |
| 275 | } | ||
| 276 | |||
| 277 | wakaba | 1.9 | sub print_error { |
| 278 | my ($msg) = @_; | ||
| 279 | w | 1.43 | &print_header($PageName{ErrorPage}, -noindex => 1); |
| 280 | wakaba | 1.16 | print qq(<p><strong class="error">$msg</strong></p>); |
| 281 | w | 1.43 | &print_footer($PageName{ErrorPage}); |
| 282 | wakaba | 1.9 | exit(0); |
| 283 | } | ||
| 284 | |||
| 285 | wakaba | 1.27 | sub print_header ($;%) { |
| 286 | my ($page, %option) = @_; | ||
| 287 | wakaba | 1.41 | my @head; |
| 288 | $option{body_class} = &is_frozen($page) ? 'frozen' : 'normal'; | ||
| 289 | $option{body_class} .= " wiki-page-obsoleted" if $option{-content_format} =~ /obsoleted="yes"/; | ||
| 290 | wakaba | 1.32 | if ($option{-goto}) { |
| 291 | wakaba | 1.36 | if ($UA =~ m#Opera|MSIE 2\.#) { |
| 292 | wakaba | 1.41 | ## WARNING: This code may output unsafe HTML document if |
| 293 | ## $option{-goto} is not clean. | ||
| 294 | wakaba | 1.36 | $option{-goto} =~ tr/;/&/ if $UA =~ m#Opera#; |
| 295 | wakaba | 1.33 | print qq{Refresh: 0; url=$option{-goto}\n}; |
| 296 | wakaba | 1.41 | push @head, qq(<meta http-equiv="refresh" content="0; url=$option{-goto}">); |
| 297 | wakaba | 1.32 | } else { |
| 298 | wakaba | 1.36 | $option{-goto} =~ tr/;/&/ if $UA =~ m#Mozilla/[1-4]\.#; |
| 299 | wakaba | 1.33 | print qq{Refresh: 0; url="$option{-goto}"\n}; |
| 300 | wakaba | 1.41 | push @head, qq(<meta http-equiv="refresh" content="0; url="@{[&escape($option{-goto})]}"">); |
| 301 | wakaba | 1.32 | } |
| 302 | } | ||
| 303 | wakaba | 1.41 | print qq{Last-Modified: @{[scalar gmtime $option{-last_modified}]}\n} if $option{-last_modified}; |
| 304 | wakaba | 1.42 | if ($option{-expires}) { |
| 305 | print qq{Expires: @{[scalar gmtime $option{-expires}]}\n}; | ||
| 306 | } | ||
| 307 | wakaba | 1.32 | if ($UA =~ m#Mozilla/2#) { |
| 308 | wakaba | 1.41 | my $ct = qq{text/html; charset=@{[&get_charset_name($kanjicode,compatible=>1)]}}; |
| 309 | print qq{Content-Type: $ct\n}; | ||
| 310 | push @head, qq{<meta http-equiv="content-type" content="$ct">}; | ||
| 311 | wakaba | 1.36 | } elsif ($UA =~ m#Infomosaic#) { |
| 312 | print qq{Content-Type: text/html\n}; | ||
| 313 | wakaba | 1.32 | } else { |
| 314 | wakaba | 1.41 | print qq{Content-Type: text/html; charset=@{[&get_charset_name($kanjicode)]}\n}; |
| 315 | wakaba | 1.32 | } |
| 316 | wakaba | 1.41 | push @head, qq(<title>@{[&escape($page)]}</title>); |
| 317 | wakaba | 1.42 | if ($UA !~ m#Mozilla/[1-4]\.# || $UA =~ m#MSIE (?:[4-9]\.|\d\d)#) { |
| 318 | push @head, qq(<link rel="stylesheet" type="text/css" href="@{[&escape($uri{wiki}.'?mycmd=TEXT_CSS;mypage='.&encode($PageName{DefaultStyleForHTML}).';x-lm='.&get_info($PageName{DefaultStyleForHTML}, $info_LastModified))]}"); | ||
| 319 | } | ||
| 320 | wakaba | 1.41 | push @head, q(<meta name="ROBOTS" content="NOINDEX">) if $option{-noindex}; |
| 321 | wakaba | 1.36 | my ($Links, $links) = &make_navigate_links ($page); |
| 322 | wakaba | 1.41 | #print $Links; ## Link: fields |
| 323 | $links = join "\n", (@head, $links); | ||
| 324 | wakaba | 1.9 | print <<"EOD"; |
| 325 | wakaba | 1.18 | Content-Language: $lang |
| 326 | Content-Style-Type: text/css | ||
| 327 | wakaba | 1.9 | |
| 328 | wakaba | 1.41 | <!-- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" |
| 329 | "http://www.w3.org/TR/html4/loose.dtd"> + RUBY --> | ||
| 330 | <html lang="$lang" class="$option{body_class}"> | ||
| 331 | wakaba | 1.36 | <head profile="http://suika.fam.cx/~wakaba/-temp/wiki/wiki?WikiHTMLMetaProfile"> |
| 332 | $links | ||
| 333 | wakaba | 1.9 | </head> |
| 334 | wakaba | 1.41 | <body class="$option{body_class}"> |
| 335 | wakaba | 1.22 | EOD |
| 336 | wakaba | 1.41 | &print_navigate_links ($page); |
| 337 | print <<EOD; | ||
| 338 | wakaba | 1.29 | <h1 class="header">@{[&escape($page)]}</h1> |
| 339 | wakaba | 1.22 | EOD |
| 340 | } | ||
| 341 | |||
| 342 | wakaba | 1.41 | sub get_charset_name ($;%) { |
| 343 | my ($charset, %option) = (lc shift, @_); | ||
| 344 | if ($charset =~ 'euc') { | ||
| 345 | $charset = $option{compatible} ? 'x-euc-jp' : 'euc-jp'; | ||
| 346 | } elsif ($charset =~ 'sjis' || $charset =~ 'shift') { | ||
| 347 | $charset = $option{compatible} ? 'x-sjis' : 'shift_jis'; | ||
| 348 | } elsif ($charset =~ 'jis') { | ||
| 349 | $charset = 'iso-2022-jp'; | ||
| 350 | wakaba | 1.32 | } |
| 351 | $charset; | ||
| 352 | } | ||
| 353 | |||
| 354 | wakaba | 1.22 | sub print_navigate_links (@) { |
| 355 | my ($page) = @_; | ||
| 356 | wakaba | 1.41 | my $editable = (&is_editable($page) && !&is_frozen($page)) ? 1 : 0; |
| 357 | my $cookedpage = &encode($page); | ||
| 358 | wakaba | 1.22 | print <<EOH; |
| 359 | wakaba | 1.16 | <div class="tools"> |
| 360 | @{[ $editable | ||
| 361 | wakaba | 1.41 | ? qq(<a title="@{[&Resource('EditThisPageLong',escape=>1)]}" href="$url_cgi?mycmd=edit;mypage=$cookedpage" accesskey="E" class="wiki-cmd">@{[&Resource('EditThisPage',escape=>1)]}</a> | ) |
| 362 | wakaba | 1.16 | : qq() |
| 363 | ]} | ||
| 364 | wakaba | 1.41 | <a href="$url_cgi?mycmd=read;mypage=$cookedpage;x-param=@{[time.[0..9]->[rand 10]]}" class="wiki-cmd" title="@{[&Resource('ViewThisPageLong',escape=>1)]}">@{[&Resource('ViewThisPage',escape=>1)]}</a> | |
| 365 | <a href="$url_cgi?mycmd=map;mypage=$cookedpage" class="wiki-cmd" title="@{[&Resource('ShowMapOfThisPageLong',escape=>1)]}">@{[&Resource('ShowMapOfThisPage',escape=>1)]}</a> | | ||
| 366 | w | 1.43 | <a href="$url_cgi?$PageName{CreatePage}" class="wiki" title="@{[&Resource('GoToCreatePageLong',escape=>1)]}">@{[&Resource('GoToCreatePage',escape=>1)]}</a> | |
| 367 | <a href="$url_cgi?$PageName{IndexPage}" class="wiki" title="@{[&Resource('GoToIndexPageLong',escape=>1)]}">@{[&Resource('GoToIndexPage',escape=>1)]}</a> | | ||
| 368 | <a href="$url_cgi?$PageName{FrontPage}" class="wiki" title="@{[&Resource('GoToHomePageLong',escape=>1)]}">@{[&Resource('GoToHomePage',escape=>1)]}</a> | | ||
| 369 | <a href="$url_cgi?$PageName{SearchPage}" class="wiki" title="@{[&Resource('GoToSearchPageLong',escape=>1)]}">@{[&Resource('GoToSearchPage',escape=>1)]}</a> | | ||
| 370 | wakaba | 1.41 | <a href="$url_cgi?mycmd=RandomJump;x-param=@{[time.[0..9]->[rand 10]]}" class="wiki randomlink" title="@{[&Resource('GoSomewhereLong',escape=>1)]}">@{[&Resource('GoSomewhere',escape=>1)]}</a> | |
| 371 | w | 1.46 | <a href="$url_cgi?$PageName{RecentChanges}" class="wiki" title="@{[&Resource('GoToRecentChangesLong',escape=>1)]}">@{[&Resource('GoToRecentChanges',escape=>1)]}</a> |
| 372 | wakaba | 1.29 | </div> |
| 373 | EOH | ||
| 374 | wakaba | 1.36 | } |
| 375 | |||
| 376 | sub make_navigate_links ($) { | ||
| 377 | my $page = shift; | ||
| 378 | my @link; | ||
| 379 | wakaba | 1.41 | push @link, {rel=>'edit', href=>"$url_cgi?mycmd=edit;mypage=@{[&encode($page)]}", class=>"wiki-command", title=>&Resource('EditThisPageLink')} if &is_editable ($page) && !&is_frozen ($page); |
| 380 | push @link, {rel=>'edit', href=>"$url_cgi?mycmd=adminedit;mypage=@{[&encode($page)]}", class=>"wiki-command", title=>&Resource('AdminEditThisPageLink')} if &is_editable ($page) || &is_frozen ($page); | ||
| 381 | push @link, {rel=>'view', href=>"$url_cgi?mycmd=read;mypage=@{[&encode($page)]};x-p=@{[time.[0..9]->[rand 10]]}", class=>'wiki-command', title=>&Resource('ViewThisPageLink')}; | ||
| 382 | push @link, {rel=>'myself', href=>"$url_cgi?@{[&encode($page)]}", class=>'wiki', title=>&Resource('GoToMyselfLink')}; | ||
| 383 | w | 1.43 | push @link, {rel=>'index', href=>"$url_cgi?$PageName{IndexPage}", class=>'wiki', title=>&Resource('GoToIndexPageLink')}; |
| 384 | push @link, {rel=>'home', href=>"$url_cgi?$PageName{FrontPage}", class=>'wiki', title=>&Resource('GoToHomePageLink')}; | ||
| 385 | wakaba | 1.41 | push @link, {rel=>'News', href=>"$url_cgi?WikiNews", class=>'wiki', title=>&Resource('GoToWikiNewsLink')}; |
| 386 | w | 1.46 | push @link, {rel=>'News', href=>"$url_cgi?$PageName{RecentChanges}", class=>"wiki", title=>&Resource('GoToRecentChangesLink')}; |
| 387 | w | 1.43 | push @link, {rel=>'News', href=>"$url_cgi?$PageName{RssPage}", class=>"wiki", title=>&Resource('GoToRssPageLink'), type=>'application/xml'}; |
| 388 | push @link, {rel=>'search', href=>"$url_cgi?$PageName{SearchPage}", class=>'wiki', title=>&Resource('GoToSearchPageLink')}; | ||
| 389 | wakaba | 1.41 | push @link, {rel=>'help', href=>"$url_cgi?WikiHelp", class=>'wiki', title=>&Resource('GoToWikiHelpLink')}; |
| 390 | push @link, {rel=>'copyright', href=>"$url_cgi?WikiPageLicense", class=>'wiki', title=>&Resource('GoToWikiPageLicenseLink')}; | ||
| 391 | push @link, {rel=>'jump', href=>qq(javascript:var%20WikiName=prompt('Please%20input%20the%20WikiName:','','Jump%20to%20SuikaWiki');if(WikiName)%7B_content.location.href='$url_cgi%3F'+encodeURIComponent(WikiName)%7D), class=>'wiki-cmd', title=>&Resource('JumpToLink')}; | ||
| 392 | push @link, {rel=>'jump', href=>qq(javascript:var%20WikiName=prompt('Please%20input%20the%20WikiName:','','Jump%20to%20SuikaWiki');if(WikiName)%7B_content.location.href='$url_cgi%3Fmycmd=edit;mypage='+encodeURIComponent(WikiName)%7D), class=>'wiki-cmd', title=>&Resource('JumpToEditLink')}; | ||
| 393 | push @link, {rel=>'lucky', href=>"$url_cgi?mycmd=RandomJump;x-param=@{[time.[0..9]->[rand 10]]}", class=>'wiki randomlink', title=>&Resource('GoSomewhereLink')}; | ||
| 394 | push @link, {rel=>'history', href=>$uri{cvs_wikipage}.do{my $s=$page;$s=~s/(.)/sprintf '%02X', ord $1/ges;$s}.'.txt', title=>&Resource('ViewHistoryOfThisPageLink'),hreflang=>'en'} if $uri{cvs_wikipage}; | ||
| 395 | w | 1.46 | push @link, {rel=>'history', href=>"$url_cgi?mycmd=diff;mypage=@{[&encode($page)]}", title=>&Resource('ViewDiffOfThisPageLink'), class=>'wiki-command'} if $wiki::diff::UseDiff; |
| 396 | wakaba | 1.41 | push @link, {rel=>'contents', href=>"$url_cgi?mycmd=map;mypage=@{[&encode($page)]}", title=>&Resource('ShowMapOfThisPageLink'), class=>'wiki-command'}; |
| 397 | wakaba | 1.36 | my ($Links, $links) = ('', ''); |
| 398 | for my $e (@link) { | ||
| 399 | $links .= qq(<link); | ||
| 400 | $Links .= qq(Link: <$e->{href}>); | ||
| 401 | for my $attr (qw/rel rev href title class type hreflang charset/) { | ||
| 402 | $links .= qq( $attr="@{[&escape($e->{$attr})]}") if $e->{$attr}; | ||
| 403 | } | ||
| 404 | for my $attr (qw/rel rev title/) { | ||
| 405 | $Links .= qq(; $attr="@{[do{$e->{$attr} =~ s/([\\\"])/\\$1/g; $e->{$attr}}]}") if $e->{$attr}; | ||
| 406 | } | ||
| 407 | $links .= qq(>\n); | ||
| 408 | $Links .= qq(\n); | ||
| 409 | } | ||
| 410 | wantarray ? ($Links, $links) : $Links; | ||
| 411 | wakaba | 1.9 | } |
| 412 | |||
| 413 | wakaba | 1.16 | sub print_footer { |
| 414 | wakaba | 1.27 | my ($page, $lm) = @_; |
| 415 | wakaba | 1.33 | my $epage = &encode ($page); |
| 416 | w | 1.45 | my $cvslog1 = q$Revision: 1.44 $; |
| 417 | my $cvslog2 = q$Date: 2003/01/01 12:30:24 $; | ||
| 418 | wakaba | 1.22 | print_navigate_links ($page); |
| 419 | print <<"EOD"; | ||
| 420 | wakaba | 1.41 | @{[ $lm ? qq(<div id="wikipage-last-modified">@{[&Resource('LastModified=',escape=>1)]}@{[&_rfc3339_date ($lm)]}</div>) : '' ]} |
| 421 | wakaba | 1.16 | <div class="footer"> |
| 422 | wakaba | 1.41 | <a href="http://www.hyuki.com/yukiwiki/" title="YukiWiki 2.0.beta1.2002-05-29 © 2000-2002 by Hiroshi Yuki">@{[&Resource('About:Name:YukiWiki',escape=>1)]}</a> <a href="http://digit.que.ne.jp/work/" title="WalWiki 2.0.beta1.wal.1 © 2000-2002 by Makio Tsukamoto">@{[&Resource('About:Name:WalWiki',escape=>1)]}</a> |
| 423 | <a href="/gate/cvs/wakaba/wiki/" title="@{[&Resource('About:SuikaWiki:JumpToCVS',escape=>1)]} ($cvslog2)">@{[&Resource('About:Name:SuikaWiki',escape=>1)]} $cvslog1</a> | ||
| 424 | wakaba | 1.18 | </div> |
| 425 | wakaba | 1.16 | </body> |
| 426 | </html> | ||
| 427 | wakaba | 1.12 | EOD |
| 428 | wakaba | 1.9 | } |
| 429 | |||
| 430 | wakaba | 1.16 | sub escape { |
| 431 | my $s = shift; | ||
| 432 | $s =~ s|\r\n|\n|g; | ||
| 433 | wakaba | 1.23 | $s =~ s|&|&|g; |
| 434 | wakaba | 1.16 | $s =~ s|<|<|g; |
| 435 | $s =~ s|>|>|g; | ||
| 436 | $s =~ s|"|"|g; | ||
| 437 | return $s; | ||
| 438 | } | ||
| 439 | |||
| 440 | sub unescape { | ||
| 441 | my $s = shift; | ||
| 442 | # $s =~ s|\n|\r\n|g; | ||
| 443 | wakaba | 1.23 | $s =~ s|<|<|g; |
| 444 | $s =~ s|>|>|g; | ||
| 445 | $s =~ s|"|"|g; | ||
| 446 | $s =~ s|&|&|g; | ||
| 447 | wakaba | 1.16 | return $s; |
| 448 | } | ||
| 449 | |||
| 450 | wakaba | 1.26 | sub print_content ($;$) { |
| 451 | wakaba | 1.27 | my ($rawcontent, %option) = @_; |
| 452 | wakaba | 1.29 | print &text_to_html($rawcontent, toc=>1, %option); |
| 453 | wakaba | 1.9 | } |
| 454 | |||
| 455 | wakaba | 1.16 | sub text_to_html { |
| 456 | my ($txt, %option) = @_; | ||
| 457 | wakaba | 1.29 | my @toc; |
| 458 | my @toc2 = @{$option{-toc}||[]}; | ||
| 459 | wakaba | 1.16 | my $tocnum = 0; |
| 460 | wakaba | 1.41 | |
| 461 | ## Load constants | ||
| 462 | my %const; | ||
| 463 | if ($option{content_format} =~ /import="([^"]+)"/) { | ||
| 464 | for (split /\s*,\s*/, $1) { | ||
| 465 | my $wp = $database{$_}; | ||
| 466 | if ($wp =~ m!^\#\?SuikaWikiConst/1.0!) { | ||
| 467 | wiki::suikawikiconst::to_hash ($wp => \%const); | ||
| 468 | } | ||
| 469 | } | ||
| 470 | } | ||
| 471 | |||
| 472 | $txt =~ s{__&&([^&]+)&&__}{defined $const{$1}?$const{$1}:qq(__&&$1&&__)}ge; | ||
| 473 | my (@txt) = split(/\n/, $txt); | ||
| 474 | wakaba | 1.16 | my (@saved, @result); |
| 475 | unshift(@saved, "</p>"); | ||
| 476 | push(@result, "<p>"); | ||
| 477 | wakaba | 1.9 | foreach (@txt) { |
| 478 | chomp; | ||
| 479 | wakaba | 1.25 | if (/^\*\*\*\*\*([^\x0D\x0A]*)/) { |
| 480 | push(@toc, qq(----- <a href="#i$tocnum">@{[&escape($1)||$tocnum]}</a>\n)); | ||
| 481 | wakaba | 1.41 | push(@result, splice(@saved), qq(<h6 @{[&id_and_name("i$tocnum")]}>) . &inline($1, const => \%const) . '</h6>'); |
| 482 | wakaba | 1.18 | $tocnum++; |
| 483 | wakaba | 1.25 | } elsif (/^\*\*\*\*([^\x0D\x0A]*)/) { |
| 484 | push(@toc, qq(---- <a href="#i$tocnum">@{[&escape($1)||$tocnum]}</a>\n)); | ||
| 485 | wakaba | 1.41 | push(@result, splice(@saved), qq(<h5 @{[&id_and_name("i$tocnum")]}>) . &inline($1, const => \%const) . '</h5>'); |
| 486 | wakaba | 1.18 | $tocnum++; |
| 487 | wakaba | 1.25 | } elsif (/^\*\*\*([^\x0D\x0A]*)/) { |
| 488 | push(@toc, qq(--- <a href="#i$tocnum">@{[&escape($1)||$tocnum]}</a>\n)); | ||
| 489 | wakaba | 1.41 | push(@result, splice(@saved), qq(<h4 @{[&id_and_name("i$tocnum")]}>) . &inline($1, const => \%const) . '</h4>'); |
| 490 | wakaba | 1.18 | $tocnum++; |
| 491 | wakaba | 1.25 | } elsif (/^\*\*([^\x0D\x0A]*)/) { |
| 492 | wakaba | 1.16 | # if (/^\*\*(.*)/) { |
| 493 | # Walrus mod (6) end | ||
| 494 | wakaba | 1.25 | push(@toc, qq(-- <a href="#i$tocnum">@{[&escape($1)||$tocnum]}</a>\n)); |
| 495 | wakaba | 1.41 | push(@result, splice(@saved), qq(<h3 @{[&id_and_name("i$tocnum")]}>) . &inline($1, const => \%const) . '</h3>'); |
| 496 | wakaba | 1.16 | $tocnum++; |
| 497 | wakaba | 1.25 | } elsif (/^\*([^\x0D\x0A]*)/) { |
| 498 | push(@toc, qq(- <a href="#i$tocnum">@{[&escape($1)||$tocnum]}</a>\n)); | ||
| 499 | wakaba | 1.41 | push(@result, splice(@saved), qq(<h2 @{[&id_and_name("i$tocnum")]}>) . &inline($1, const => \%const) . '</h2>'); |
| 500 | wakaba | 1.16 | $tocnum++; |
| 501 | wakaba | 1.25 | } elsif (/^(={1,6})(.*)/) { |
| 502 | wakaba | 1.18 | &back_push('ol', length($1), \@saved, \@result); |
| 503 | wakaba | 1.41 | push(@result, '<li>' . &inline($2, const => \%const) . '</li>'); |
| 504 | wakaba | 1.25 | } elsif (/^(-{1,6})(.*)/) { |
| 505 | wakaba | 1.28 | &back_push('ul', length($1), \@saved, \@result); |
| 506 | my ($pf, $l) = ('', $2); | ||
| 507 | if (!$main::_EMBEDED && $l =~ s/^\s*\[([0-9]+)\]//) { | ||
| 508 | my $num = 0+$1; | ||
| 509 | $pf = qq(<a name="anchor-$num" id="anchor-$num" class="anchor">[$num]</a>); | ||
| 510 | } | ||
| 511 | wakaba | 1.41 | push(@result, '<li>' . $pf . &inline ($l, const => \%const) . '</li>'); |
| 512 | wakaba | 1.9 | } elsif (/^:([^:]+):(.*)/) { |
| 513 | wakaba | 1.16 | &back_push('dl', 1, \@saved, \@result); |
| 514 | wakaba | 1.41 | push(@result, '<dt>' . &inline($1, const => \%const) . '</dt>', '<dd>' . &inline($2, const => \%const) . '</dd>'); |
| 515 | wakaba | 1.30 | } elsif (/^(?!>>\d)(>{1,5})(.*)/) { |
| 516 | wakaba | 1.16 | &back_push('blockquote', length($1), \@saved, \@result); |
| 517 | wakaba | 1.30 | push @result, "<p>"; |
| 518 | wakaba | 1.41 | push(@result, &inline($2, const => \%const)); |
| 519 | wakaba | 1.30 | unshift @saved, "</p>"; |
| 520 | wakaba | 1.9 | } elsif (/^\s*$/) { |
| 521 | push(@result, splice(@saved)); | ||
| 522 | wakaba | 1.30 | push(@result, "<p>"); |
| 523 | wakaba | 1.9 | unshift(@saved, "</p>"); |
| 524 | } elsif (/^(\s+.*)$/) { | ||
| 525 | wakaba | 1.16 | &back_push('pre', 1, \@saved, \@result); |
| 526 | wakaba | 1.41 | push(@result, &inline($1, const => \%const)); |
| 527 | } elsif (/^\,(.*?)[\x0D\x0A]*$/) { | ||
| 528 | &back_push('table', 1, \@saved, \@result); | ||
| 529 | wakaba | 1.16 | ####### |
| 530 | # This part is taken from Mr. Ohzaki's Perl Memo and Makio Tsukamoto's WalWiki. | ||
| 531 | # XXXXX | ||
| 532 | my $tmp = "$1,"; | ||
| 533 | my @value = map {/^"(.*)"$/ ? scalar($_ = $1, s/""/"/g, $_) : $_} ($tmp =~ /("[^"]*(?:""[^"]*)*"|[^,]*),/g); | ||
| 534 | my @align = map {(s/^\s+//) ? ((s/\s+$//) ? ' align="center"' : ' align="right"') : ''} @value; | ||
| 535 | my @colspan = map {($_ eq '==') ? 0 : 1} @value; | ||
| 536 | for (my $i = 0; $i < @value; $i++) { | ||
| 537 | if ($colspan[$i]) { | ||
| 538 | while ($i + $colspan[$i] < @value and $value[$i + $colspan[$i]] eq '==') { | ||
| 539 | $colspan[$i]++; | ||
| 540 | } | ||
| 541 | $colspan[$i] = ($colspan[$i] > 1) ? sprintf(' colspan="%d"', $colspan[$i]) : ''; | ||
| 542 | wakaba | 1.41 | $value[$i] = sprintf('<td%s%s>%s</td>', $align[$i], $colspan[$i], &inline($value[$i], const => \%const)); |
| 543 | wakaba | 1.16 | } else { |
| 544 | $value[$i] = ''; | ||
| 545 | } | ||
| 546 | } | ||
| 547 | push(@result, join('', '<tr>', @value, '</tr>')); | ||
| 548 | # XXXXX | ||
| 549 | ####### | ||
| 550 | wakaba | 1.29 | } elsif (/^\[(INS|DEL|PRE)\[\s*$/) { |
| 551 | wakaba | 1.28 | push @result, splice (@saved), '<'.lc($1).'>'; |
| 552 | unshift @saved, "</p>"; | ||
| 553 | push @result, "<p>"; | ||
| 554 | wakaba | 1.29 | } elsif (/^\](INS|DEL|PRE)\]\s*$/) { |
| 555 | wakaba | 1.28 | push @result, splice (@saved), '</'.lc($1).'>'; |
| 556 | } elsif (/^\[([0-9]+)\](.*)$/ && !$main::_EMBEDED) { | ||
| 557 | my $num = 0+$1; | ||
| 558 | push @result, qq(<a name="anchor-$num" id="anchor-$num" class="anchor">[$num]</a>); | ||
| 559 | wakaba | 1.41 | push @result, &inline ($2, const => \%const); |
| 560 | wakaba | 1.9 | } else { |
| 561 | wakaba | 1.41 | push(@result, &inline($_, const => \%const)); |
| 562 | wakaba | 1.9 | } |
| 563 | } | ||
| 564 | push(@result, splice(@saved)); | ||
| 565 | wakaba | 1.27 | |
| 566 | my $toc = ''; | ||
| 567 | wakaba | 1.16 | if ($option{toc}) { |
| 568 | # Convert @toc (table of contents) to HTML. | ||
| 569 | # This part is taken from Makio Tsukamoto's WalWiki. | ||
| 570 | my (@tocsaved, @tocresult); | ||
| 571 | wakaba | 1.29 | foreach (@toc,@toc2) { |
| 572 | wakaba | 1.25 | if (/^(-{1,6})(.*)$/) { |
| 573 | wakaba | 1.16 | &back_push('ul', length($1), \@tocsaved, \@tocresult); |
| 574 | push(@tocresult, '<li>' . $2 . '</li>'); | ||
| 575 | } | ||
| 576 | } | ||
| 577 | push(@tocresult, splice(@tocsaved)); | ||
| 578 | wakaba | 1.27 | $toc = join("\n", @tocresult); |
| 579 | $toc = $toc ? qq(<div id="wikipage-toc">$toc</div>) : ''; | ||
| 580 | wakaba | 1.16 | } |
| 581 | wakaba | 1.30 | $toc .= join("\n", @result); |
| 582 | $toc =~ s#<p>\n</p>##g; | ||
| 583 | $toc =~ s#[\x0D\x0A]+</#</#g; | ||
| 584 | $toc =~ s#<pre>\n#<pre>#g; | ||
| 585 | $toc; | ||
| 586 | wakaba | 1.9 | } |
| 587 | |||
| 588 | sub back_push { | ||
| 589 | wakaba | 1.16 | my ($tag, $level, $savedref, $resultref, $attr) = @_; |
| 590 | while (@$savedref > $level) { | ||
| 591 | push(@$resultref, shift(@$savedref)); | ||
| 592 | } | ||
| 593 | if ($savedref->[0] ne "</$tag>") { | ||
| 594 | push(@$resultref, splice(@$savedref)); | ||
| 595 | } | ||
| 596 | while (@$savedref < $level) { | ||
| 597 | unshift(@$savedref, "</$tag>"); |