#!/usr/local/bin/perl ########################################################## my $scriptname = "rss.cgi"; my $scriptversion = "0.6.4"; my $scriptlastupdate = "2006-11-23"; my $scripturl = "http://sibelian.jp.land.to/texqa/"; ########################################################## $t1 = (times)[0]; # パッケージの読み込み use Encode qw/ from_to /; # 文字コード変換に Encode を利用 use URI::Fetch; # 外部ページ読み込みに URI::Fetch を利用 use XML::TreePP; # XML の読み込みにXML::TreePP を利用 use Time::Local; # localtime → time 変換 # 各種設定 my $encode = "UTF-8"; # 出力文字コードは UTF-8 (変更不可) my $logfile = "./log.xml"; # ログ my $messagesfile = "./messages.xml"; # キャッシュ my $lockfile = "./rss.lock"; # ロックファイル(中身は空) my $messagesfilelength = "400"; # キャッシュする件数 my $baseurl = "http://oku.edu.mie-u.ac.jp/~okumura/texfaq/qa/"; # index.html のあるディレクトリ my $descriptionlinemaximum = 10; # に表示する最大の行数 my $updateperiod = 300; # 更新間隔(秒) my $presenttime = time(); # 現在時刻 # 配信件数を取得。デフォルトは 10 件 # オプション f をつけると、強制更新(テスト用) # オプション n をつけると、カウンタを回さない(作者用、ユーザーは使わないで!)。 # オプション a をつけると、本文全文を配信(つけないと $descriptionlinemaximum 行の要約のみ) # オプション l をつけると、リスト(番号、日時、タイトル、名前)のみを表示 # オプション s をつけると、リンクをスレッド XHTML のものに変更 # オプション t をつけると、 $num 番の書き込みを含むスレッドを表示 # オプション th をつけると、 $num 番の書き込みを含むスレッドを XHTML 1.0 形式で出力(新しいものから) # オプション tho をつけると、 $num 番の書き込みを含むスレッドを XHTML 1.0 形式で出力(古いものから) $num = 0; $num = $ARGV[0]; $force = 0; $nocount = 0; $all = 0; $list = 0; $sibelian = 0; $thread = 0; $html = 0; $old = 0; if($num=~ /f/) {$num =~ s/f//; $force = 1;} if($num=~ /n/) {$num =~ s/n//; $nocount = 1;} if($num=~ /a/) {$num =~ s/a//; $all = 1;} if($num=~ /l/) {$num =~ s/l//; $list = 1;} if($num=~ /s/) {$num =~ s/s//; $sibelian = 1;} if($num=~ /t/) {$num =~ s/t//; $thread = 1;} if($num=~ /h/) {$num =~ s/h//; $html = 1;} if($num=~ /o/) {$num =~ s/o//; $old = 1;} if(($num <= 0) || ($num =~ /[^0-9]/)) { $num = 10;} # 暫定的な利用調査 #sub get_ipaddr_hash { # my $ipaddr = $ENV{REMOTE_ADDR} or return; # my $intaddr = unpack( N => pack( C4 => reverse split( /\./, $ipaddr ))); # my $hashval = 10000 + $intaddr % 90000; # $hashval; #} #if (!$nocount) { # open(OUT, ">> log.txt"); # flock(OUT, 2); # seek(OUT, 0, 2); # print OUT "$presenttime,".&get_ipaddr_hash().",$ARGV[0],$ENV{HTTP_USER_AGENT}\n"; # close(OUT); #} # 広告 sub adlist { print "\n"; } sub ad { print "\n"; print "[qa:00000] スポンサードリンク\n"; print "http://land.to/index.php\n"; print "\n"; print "<iframe src=\"http://jp.land.to/ad/adpc_if.p\" name=\"AD\" id=\"AD\" title=\"ad\" width=\"468\" height=\"70\" marginwidth=\"0\" marginheight=\"0\" frameborder=\"0\" scrolling=\"no\"><a href=\"http://land.to/index.php\">Land.to</a></iframe>\n"; # $t2 = (times)[0]; # $t3 = $t2 - $t1; # print "<br />[".sprintf("%.2f",$t3)." sec]"; print "\n"; print "Land.to\n"; print "".&W3CDTF6time($presenttime)."\n"; print "\n"; } # お知らせ sub announcementlist { print "\n"; } sub announcement { print "\n"; print "[qa:INFO5] 本文を含まないリストの配信をサポートしました。\n"; print "$scripturl"."index.html#history20061007\n"; print "\n"; print "詳しくは <a href=\"$scripturl\">$scripturl</a> をご覧ください。<br />\n"; print "\n"; print "\n"; print "スクリプトをバージョン・アップして<br />\n"; print "本文を含まないリストの配信をサポートしました。<br />\n"; print "\"l\" オプションで、リスト(番号、日時、タイトル、名前)のみを返します。<br />\n"; print "ネットワーク資源の無駄遣いを防ぐためにも、<br />\n"; print "本文や要約がいらない方( Firefox の Live Bookmark とかをお使いの方)は<br />\n"; print "できるだけ \"l\" をつけてアクセスしてください。<br />\n"; print "詳しくは <a href=\"$scripturl\">$scripturl</a> をご覧ください。\n"; print "\n"; print "Sibelian\n"; print "".&W3CDTF6time($presenttime)."\n"; print "\n"; } # UNIX 時間→ W3CDTF (YYYY-MM-DDThh:mm:ssTZD) sub W3CDTF6time { (my $sec, my $min, my $hour, my $mday, my $mon, my $year, my $wday, my $yday, my $isdst) = localtime($_[0]); $year = 1900 + $year; $mon = sprintf("%02d", ($mon + 1)); $mday = sprintf("%02d", $mday); $hour = sprintf("%02d", $hour); $min = sprintf("%02d", $min); $sec = sprintf("%02d", $sec); return ("$year-$mon-$mday\T$hour:$min:$sec+09:00"); } # W3CDTF (YYYY-MM-DDThh:mm:ssTZD) → UNIX 時間 sub timeW3CDTF6 { if($_[0] =~ /([0-9]+)-([0-9]+)-([0-9]+)T([0-9]+):([0-9]+):([0-9]+)\+9:00/) { return (timelocal($6,$5,$4,$3,($2-1),($1-1900))); } else { return 0; } } # UNIX 時間→ 普通の時間表示 (YYYY-MM-DD hh:mm:ss) sub normaltime { (my $sec, my $min, my $hour, my $mday, my $mon, my $year, my $wday, my $yday, my $isdst) = localtime($_[0]); $year = 1900 + $year; $mon = sprintf("%02d", ($mon + 1)); $mday = sprintf("%02d", $mday); $hour = sprintf("%02d", $hour); $min = sprintf("%02d", $min); $sec = sprintf("%02d", $sec); return ("$year-$mon-$mday $hour:$min:$sec"); } # 相対 URL →絶対 URL sub absolute { if ($_[0] =~/^http|^https|^ftp|^ftps/) {"$_[0]";} else {"$baseurl$_[0]";} } # アイテム要素を出力する sub item { print "{link}\">\n"; print "[qa:$_[0]->{id}] $_[0]->{title}\n"; print "$_[0]->{link}\n"; if(!$list) { print "\n"; # print "名前:$_[0]->{author}\<br \/\>\n"; my @textlines = split(/\n/, $_[0]->{text}); foreach (@textlines) { ~ s/&/&amp;/g; ~ s/</&lt;/g; ~ s/>/&gt;/g; ~ s//>/g; ~ s/\s*$/\n/; } print "<pre>"; my $linecount = 0; foreach (@textlines) { $linecount ++; if($linecount > $descriptionlinemaximum) { print "...\n"; last; } print; } print "</pre>"; print "\n"; if ($all) { print "\n"; print "<pre>"; foreach (@textlines) { print; } print "</pre>\n"; print "\n"; } } print "$_[0]->{author}\n"; print "".&W3CDTF6time($_[0]->{timestamp})."\n"; print "\n"; } # TeX Q & A へのリンクにするかどうかの判断してリンクアンカーを作成 sub linkqa { my $anchor = shift(@_); my $innertext = shift(@_); foreach (@_) { if ($anchor eq $_) { return("$innertext<\/a>"); } } return("$innertext<\/a>\"[qa:$1]\""); } # スクリプト本体実行の排他処理 open(LOCK,"> $lockfile"); flock(LOCK, 2); ########################################################## # スクリプト本体 ########################################################## # キャッシュを読み込み my $messagespp = XML::TreePP->new( output_encoding => $encode, force_array => [ "thread" ], first_out => ["id", "title", "link"], last_out => ["text","thread"] ); $messages = $messagespp->parsefile($messagesfile); # ログを読み込み my $logpp = XML::TreePP->new( output_encoding => $encode ); $log = $logpp->parsefile($logfile); # カウンタを回す if (!$nocount) {$log->{log}->{counter}++;} # 前回の更新時間から $updateperiod 以上経っていればキャッシュを更新 if (time() > ($log->{log}->{lastupdatetime} + $updateperiod) || $force) { # index.html を取得 my $res = URI::Fetch->fetch("$baseurl") or die URI::Fetch->errstr; # 文字コードを EUC から UTF-8 に変換 $indexpage = $res->content(); from_to ($indexpage, 'euc-jp', 'utf8'); # 行ごとに分割し、記事のリストから新しい書き込みをキャッシュに追加 my @indexlines = split(/\n/, $indexpage); @indexlines = reverse(@indexlines); my @ids; foreach my $indexline (@indexlines) { if ($indexline =~ /^([0-9]+): ([0-9]+)-([0-9]+)-([0-9]+) ([0-9]+):([0-9]+):([0-9]+) (.+)<\/a> (.+)
/) { (my $id, my $timestamp, my $link, my $title, my $author) = ("$1", &timeW3CDTF6("$2-$3-$4T$5:$6:$7+9:00"), "$8", "$9", "$10"); @ids = (@ids, $id); if ($id > $log->{log}->{lastnewestnumber}) { my $res = URI::Fetch->fetch("$baseurl$link") or die URI::Fetch->errstr; my $textpage = $res->content(); from_to ( $textpage, 'euc-jp' , 'utf8' ); my @textlines = split(/\n/, $textpage); my $flag = 0; my $text; my @referers; # リファラ foreach my $textline (@textlines) { if($textline =~ /<\/pre>/) { last; } if($flag) { # リファラを読み込む if($textline =~ /
>>(.+?)<\/a>/) { $textline =~ s/>>(.+?)<\/a>/>>$2<\/a>/g; @referers = (@referers, $1); } if($textline =~/<a href=\\\"([0-9]+?)\.html\\\">>>(.+?)<\/a>/) { $textline =~ s/<a href=\\\"([0-9]+?)\.html\\\">>>(.+?)<\/a>/>>$2<\/a>/g; @referers = (@referers, $1); } # ほかのリンクタグはそのまま( http:// がないものには $baseurl を加える) $textline =~s/(.+?)<\/a>/"$2<\/a>"/eg; # テキストを読み込む $text .= "$textline\n"; } if($textline =~ /
/) { $flag = 1; } } # リファラから、どこのスレッドなのか決定 my @thread = "#0"; if (@referers[0]) { foreach my $referer (@referers) { foreach my $message (@{$messages->{messages}->{message}}) { if ($message->{id} == $referer) { my $flag = 0; foreach $thread (@{$message->{thread}}) { foreach (@thread) { if ($thread eq "$_") { $flag =1; last; } } if (!$flag) {@thread = (@thread, "$thread");} } } } } } else { if($title =~ /^Re: (.+)/) { my $roottitle = $1; foreach my $message (@{$messages->{messages}->{message}}) { if($message->{title} eq "$roottitle" || $message->{title} eq "$title") { @thread = (@thread, "@{$message->{thread}}"); last; } } } } shift(@thread); if (!@thread) {@thread = ("\#$id");} push(@{$messages->{messages}->{message}}, {"id" => "$id", "title" => "$title", "link" => "$link", "author" => "$author", "timestamp" => "$timestamp", "text" => "$text", "thread" => [@thread]}); $log->{log}->{lastnewestnumber} = $id; } } } # スパム書き込み(欠番)を削除する my $latestid = $ids[0]; my @spamids; foreach my $id (@ids) { if (($id - $latestid) != 1) { my $i; for ($i = 0; $i < ($id - $latestid - 1); $i++) { @spamids = (@spamids, ($latestid + $i + 1)); } } $latestid = $id; } foreach my $spamid (@spamids) { my $i; for ($i = 0; $i < ($#{$messages->{messages}->{message}}+1); $i++) { if($messages->{messages}->{message}->[$i]->{id} == $spamid) { splice(@{$messages->{messages}->{message}}, $i, 1); $i--; } } } # キャッシュの数が $messagesfilelength より多ければキャッシュを削除する if ( ($#{$messages->{messages}->{message}} + 1 - $messagesfilelength)>0) { splice(@{$messages->{messages}->{message}}, 0, ($#{$messages->{messages}->{message}} + 1 - $messagesfilelength) ); } $messagespp->writefile($messagesfile, $messages, $encode ); $log->{log}->{lastupdatetime} = $presenttime; } # ログに書き込み $logpp->writefile($logfile, $log, $encode); close(LOCK); @{$messages->{messages}->{message}} = reverse(@{$messages->{messages}->{message}}); ########################################################## # 新着 RSS 出力 ########################################################## if (!$thread) { if (!$html) { print < TeX Q & A (新着) $baseurl EOF print "".&normaltime($log->{log}->{lastupdatetime})." 更新(更新後 ".($updateperiod/60)." 分間はキャッシュを配信) $log->{log}->{counter}\n"; print "\n"; print "\n"; print "\n"; #&announcementlist(); my $count = 0; foreach my $message (@{$messages->{messages}->{message}}) { $count++; if ($count > $num) { last; } if($sibelian) { $message->{link} =~ s/([0-9]+).html/$scriptname?th$1\#id$1/; } else { $message->{link} = "$baseurl$message->{link}"; } print "{link}\" />\n"; } &adlist(); print "\n"; print "\n"; print "\n"; #&announcement(); my $count = 0; foreach my $message (@{$messages->{messages}->{message}}) { $count++; if ($count > $num) { last; } &item($message); } &ad(); print "\n" }# end of if (!$html) ########################################################## # 新着 XHTML 出力 ########################################################## else { # if ($html) @{$messages->{messages}->{message}} = reverse(@{$messages->{messages}->{message}}); print < TeX Q & A (新着)

TeX Q & A

TeXフォーラム をぜひお試しください。

サイト内検索 (Powered by Google)

新しい話題の書き込みはこちらのページです。 メールサービスまでメールでお申し込みください。 SibelianさんがRSSを提供してくださっています。

TeX Wikiの質問のしかたをぜひお読みください。

スパムが急増したため少し強い制約を付けました。もし書き込めない場合はご連絡ください。

EOF if(!$list) {print "\"showall\" ";} print "書き込み一覧"; print " \n"; print "\"{rss:channel/rss:title}\"\n"; print " \n"; print "\"flat\"\n"; print "

EOF if (($#{$messages->{messages}->{message}} + 1 -$num)>0) { splice(@{$messages->{messages}->{message}}, 0, ($#{$messages->{messages}->{message}} + 1 -$num) ); } my $treepp = XML::TreePP->new( output_encoding => $encode, xml_decl => '', ignore_error => 1 ); %treelist; $treenumber = 0; sub newtag { if (($presenttime - $_[0]) < 86400) { "New!"; } } foreach my $message (@{$messages->{messages}->{message}}) { if($sibelian) { $message->{link} =~ s/([0-9]+).html/$scriptname?th$1\#id$1/; } else { $message->{link} = "$baseurl$message->{link}"; } foreach (@{$message->{thread}}) { my $threadnumber; if($_ =~/([0-9]+)/){$threadnumber = $1;} if(!exists($treelist{$_})) { $treelist{$_} = $treenumber; $treenumber++; $tree->{ul}->{li}->[$treelist{$_}]->{-style} = "margin-top: 1em;"; if ($list) { push(@{$tree->{ul}->{li}->[$treelist{$_}]->{ul}->{li}},{"#text" => "{id}\">▼ $message->{id}: ".&normaltime($message->{timestamp})." {link}\">$message->{title} $message->{author} ".&newtag($message->{timestamp})}); } else { push(@{$tree->{ul}->{li}->[$treelist{$_}]->{ul}->{li}},{"#text" => "{id}\">▼ \"showthread\"/ {id}');\" id=\"id$threadnumber-$message->{id}button\" name=\"id$threadnumber-$message->{id}button\">\"show\" $message->{id}: ".&normaltime($message->{timestamp})." {link}\">$message->{title} $message->{author} ".&newtag($message->{timestamp})."
{id}\" style=\"display:none; margin: .5em 5em .5em 3em; padding: .5em .5em .5em .5em; border: 3px solid #F99;\" class=\"content\">
$message->{text}
"}); } $tree->{ul}->{li}->[$treelist{$_}]->{-title} = "$message->{timestamp}"; } else { if($list) { push(@{$tree->{ul}->{li}->[$treelist{$_}]->{ul}->{li}},{"#text" => " └ $message->{id}: ".&normaltime($message->{timestamp})." {link}\">$message->{title} $message->{author} ".&newtag($message->{timestamp})}); } else { push(@{$tree->{ul}->{li}->[$treelist{$_}]->{ul}->{li}},{"#text" => "   └ {id}');\" id=\"id$threadnumber-$message->{id}button\" name=\"id$threadnumber-$message->{id}button\">\"show\" $message->{id}: ".&normaltime($message->{timestamp})." {link}\">$message->{title} $message->{author} ".&newtag($message->{timestamp})."
{id}\" style=\"display:none; margin: .5em 5em .5em 3em; padding: .5em .5em .5em .5em; border: 3px solid #F99;\" class=\"content\">
$message->{text}
"}); } $tree->{ul}->{li}->[$treelist{$_}]->{ul}->{li}->[0]->{"#text"} =~ s/tho[0-9]+/tho$message->{id}/; $tree->{ul}->{li}->[$treelist{$_}]->{-title} = "$message->{timestamp}"; } } } #@{$tree->{ul}->{li}} = reverse(@{$tree->{ul}->{li}}); my $temptree; my $i,$j; for ($j=0;$j<($#{$tree->{ul}->{li}});$j++) { for ($i=0;$i<($#{$tree->{ul}->{li}}-$j); $i++) { if ($tree->{ul}->{li}->[$i]->{-title} < $tree->{ul}->{li}->[$i+1]->{-title}) { $temptree = $tree->{ul}->{li}->[$i]; $tree->{ul}->{li}->[$i] = $tree->{ul}->{li}->[$i+1]; $tree->{ul}->{li}->[$i+1] = $temptree; } } } $source = $treepp->write( $tree, $encode ); $source =~ s/<//g; $source =~ s/"/"/g; $source =~ s/'/'/g; $source =~ s/&/&/g; $source =~ s/title=".+?"//g; print "$source"; print "
\n"; print "
".&normaltime($log->{log}->{lastupdatetime})." 更新(更新後 ".($updateperiod/60)." 分間はキャッシュを配信) $log->{log}->{counter}
\n"; $t2 = (times)[0]; $t3 = $t2 - $t1; print "Generated by $scriptname $scriptversion ($scriptlastupdate) [".sprintf("%.2f",$t3)." sec]\n"; if ($ENV{HTTP_USER_AGENT} =~ /MSIE/) { print " \"Get\n"; } print "
\n"; print "\n"; print "\n"; }# end of if ($html) } # end of if (!$thread) ########################################################## # スレッド RSS 出力 ########################################################## elsif ($thread) { # if ($thread) # どのスレッドに属するかを調べる my @threads; @threads = "\#0"; foreach my $message (@{$messages->{messages}->{message}}) { if ($message->{id} == $num) { @threads = @{$message->{thread}}; } } if(!$html) { print < EOF if (@threads[0] eq "\#0") { print "TeX Q & A (該当するスレッドが見つかりません)\n"; } else { print "TeX Q & A (スレッド @threads )\n"; } print "$scripturl$scriptname?th$num\n"; print "".&normaltime($log->{log}->{lastupdatetime})." 更新(更新後 ".($updateperiod/60)." 分間はキャッシュを配信) $log->{log}->{counter}\n"; print "\n"; print "\n"; print "\n"; #&announcementlist(); foreach my $message (@{$messages->{messages}->{message}}) { foreach $thread (@threads) { foreach (@{$message->{thread}}) { if($thread eq "$_"){ if($sibelian) { $message->{link} =~ s/([0-9]+).html/$scriptname?th$1\#id$1/; } else { $message->{link} = "$baseurl$message->{link}"; } print "{link}\" />\n"; last; } } } } &adlist(); print "\n"; print "\n"; print "\n"; #&announcement(); foreach my $message (@{$messages->{messages}->{message}}) { foreach $thread (@threads) { foreach (@{$message->{thread}}) { if($thread eq "$_"){ &item($message); last; } } } } &ad(); print "\n" } # end of if(!$html) ########################################################## # スレッド XHTML 出力 ########################################################## else { # if($html) # 古いものから表示する場合 if ($old) { @{$messages->{messages}->{message}} = reverse(@{$messages->{messages}->{message}}); } print < EOF if ($threads[0] eq "\#0") { print "TeX Q & A (該当するスレッドが見つかりません)\n"; } else { print "TeX Q & A (スレッド @threads )\n"; } print < EOF print "

TeX Q & A

\n"; if ($threads[0] eq "\#0") { print "

大量破壊兵器はイラクには見つかりませんでした。

\n"; if ($num != 10) { print "

指定した書き込みが古すぎるかもしれません。
\n"; print "$baseurl$num.html をお探しですか?

\n"; } else { print "

たぶん、スレッドの指定方法が間違っています。
\n"; print "$scripturl を読んで出直してきてください。

\n"; } } else { print "

スレッド @threads \"TeX

\n"; print "
    \n"; my @threadlist; foreach my $message (@{$messages->{messages}->{message}}) { my $flag = 0; foreach my $thread (@threads) { foreach (@{$message->{thread}}) { if($thread eq "$_"){ $flag = 1; } } } if($flag) { if (!$old) { if ($message->{id} == $num) { print "
  • [qa:$message->{id}] {id}\">$message->{title} $message->{author}
  • \n"; } else { print "
  • [qa:$message->{id}] {id}\">$message->{title} $message->{author}
  • \n"; } } else { if (!@threadlist) { if ($message->{id} == $num) { print "
  • [qa:$message->{id}] {id}\">$message->{title} $message->{author}
  • "; } else { print "
  • [qa:$message->{id}] {id}\">$message->{title} $message->{author}
  • "; } } else { if ($message->{id} == $num) { print "
  • [qa:$message->{id}] {id}\">$message->{title} $message->{author}
  • "; } else { print "
  • └ [qa:$message->{id}] {id}\">$message->{title} $message->{author}
  • "; } } } @threadlist = (@threadlist, $message->{id}); } } print "
\n"; print "
\n"; foreach my $message (@{$messages->{messages}->{message}}) { my $flag = 0; foreach my $thread (@threads) { foreach (@{$message->{thread}}) { if($thread eq "$_"){ $flag = 1; } } } if($flag) { if ($message->{id} == $num) { print "

{id}\" name=\"id$message->{id}\">[qa:$message->{id}] $message->{title} \"スレッド {id}.html\">\"[qa:$message-{id}] $message->{title}\" border=\"0\" />

\n"; } else { print "

{id}\" name=\"id$message->{id}\">[qa:$message->{id}] $message->{title} \"スレッド {id}.html\">\"[qa:$message-{id}] $message->{title}\" border=\"0\" />

\n"; } print "

名前:$message->{author}
\n"; print "日時:".&normaltime($message->{timestamp})."

\n"; print "
\n";
			my @textlines = split(/\n/, $message->{text});
			foreach my $textline (@textlines) {
				
				# リンクアンカーの処理
				$textline =~ s/(.+?)<\/a>/&linkqa($1,$2,@threadlist)/eg;
				$textline =~ s/\s*$/\n/;
				print $textline;
			}
			print "
\n"; } } } print "
\n"; print "
\n"; print "\n"; print "\n"; } # end of if($html) } # end of if($thread)