SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

PerlでデスクトップCGI

デスクトップCGIでWebとデスクトップを融合する 第1回

Firefox/Apache用AtomリーダーCGIの作成


  • X ポスト
  • このエントリーをはてなブックマークに追加

AtomフィードをHTMLに変換表示する「atom2html.cgi」を作成する

概要

 「atom2html.cgi」は、「feeds.cgi」と比べるとスクリプトの規模が大きいので、スクリプトが何をしているのか、まず簡単に解説してから、個々の注意点について説明していきます。動作は次のような流れになります。

  1. CGIモジュールのparam()で、AtomフィードのURLを格納したatomパラメータ値を得ます。
  2. CGI出力部分の最初の部分を出力しておきます。
  3. LWPモジュールのget()を使って、Atomフィードのファイルをインターネットから取得します。
  4. 最初のXML宣言行のencoding属性から、エンコーディングを取得します。
  5. encoding属性値のないものは、UTF-8を仮定します。
  6. Unicode文字の16進表記があれば、UTF-8文字に変換します。
  7. エンコーディングがUTF-8でなければ、Encodeモジュールのfrom_to()でutf8に変換します。
  8. Atomフィードのファイルのタグの後にある改行と改行後の空白文字列を削除し、preタグの間のデータの後にある改行を除いて、改行を含まない塊にします。
  9. XMLタグの前後とCDATAセクションの解析に必要な部分すべてに改行を挿入します。
  10. Atomフィードの文字列の塊を改行で分割して、配列に格納します。
  11. foreachループで配列から一行ずつ取り出し、各要素タグの開始と終了のパターンマッチを使って、各要素の値を、要素名をキーにして取得していきます。キーとなる要素名には同じものが存在するので、feed主要素とentry主要素に分けて、%feed%entryに格納していきます。
  12. CDATAセクションのデータは、ループの後の処理を妨害しないように実体参照エンコードします。
  13. content要素中のpreタグの間にあるデータ行には改行を付加していきます。
  14. Atomフィードはentry主要素単位でまとまりになりますから、entry主要素の終了タグの行で、%entryに格納した各副要素をタブ区切りの文字列にまとめて、@entries配列に格納していきます。
  15. feed主要素は1回しか現れないので、foreach終了後に、Atomのバージョンに従って、%feedの内容を整形して、HTML出力します。
  16. entry主要素の内容の入った@entriesから、foreachで一つずつ取り出しながら、タブ区切りで副要素を取り出しながら整形して、HTMLの表出力を繰り返します。
  17. HTML出力する前に、各要素の内容に実体参照が含まれている場合には、実体参照をデコードしてから処理します。この際、content要素にpreタグがある場合には、実体参照のデコードは行いません。

 以上のスクリプトの流れと次項のAtomフィードの形式を頭に入れてから、スクリプトのコメントを参考に流れを追ってみてください。原理的には、「feeds.cgi」で説明した行単位の入力のループの中で動作を変えるテキスト処理です。取得すべき要素の数が多く、しかも形態が多様であり、属性の取得等も考慮しているために、ループ中の条件判定が複雑になっています。

Atomフィードの例

 スクリプトの理解を深めるためには、入力するAtomフィードのファイル内容を知る必要があります。ここで、Atomフィードの要素を説明するよりは、実際のサンプルを見るのが理解も早いでしょう。著者のサイトのAtomフィードの一部を示しておきます。entry要素を一つだけ示しています。

著者サイトのAtomフィード
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>日曜プログラマのひとりごと</title>
    <subtitle>TS Networkのために - 更新日記</subtitle>
    <updated>2006-05-13T00:16:35+09:00(JST)</updated>
    <link rel="self" type="application/atom+xml"
        href="http://homepage1.nifty.com/kazuf/renewal_atom.xml"/>
    <link rel="alternate" type="text/html" hreflang="ja"
        href="http://homepage1.nifty.com/kazuf/renewal.html"/>
    <rights>Copyright (C) 2006 Kazuo Fujioka</rights>
    <generator uri="http://homepage1.nifty.com/kazuf/renewal.html"
        version="1.0">
        rn2atom.cgi
    </generator>
    <entry>
        <title>atom2html.cgi v0.92リリース</title>
        <link rel="alternate" type="text/html"
            href="http://homepage1.nifty.com/kazuf/renewal.html#rn_1147266376"/>
        <published>2006-05-10</published>
        <updated></updated>
        <category>更新履歴</category>
        <author>
             <name>Kazuo Fujioka</name>
        </author>
        <content type="xhtml" xml:lang="ja"
            xml:base="http://homepage1.nifty.com/kazuf/">
        <div xmlns="http://www.w3.org/1999/html">
        <div class="emph">
        <a href="http://homepage1.nifty.com/kazuf/renewal_index.html#rn">
            [更新履歴]</a>
        <a name="rn_1147266376" id="rn_1147266376">
            atom2html.cgi v0.92リリース</a></div>
        <p>
            改良点は次のとおり。本スクリプトはjperl用です。
            → <a href="http://homepage1.nifty.com/kazuf/atom2html.html">
atom2html</a>
        </p>
        <ul>
        <li>Unicode文字16進表記のUnicode文字への変換。</li>
        <li>CDATAセクションの実体参照の二重エンコードのデコードに対応。</li>
        </ul><br />
        </div>
        </content>
    </entry>
</feed>

 Atomのフォーマットには、0.3と1.0があります。1.0の詳細は、参考資料にある『The Atom Syndication Format(RFC4287)』を参照してください。後で紹介する「atom2html.cgi」スクリプトは、0.3と1.0の両方に対応するように書いています。コメントを参考にしてスクリプトを見れば、バージョンによる要素の違いも分かります。

LWPモジュール

 LWPモジュールの手軽さは特筆すべきものです。インターネット上のURLで表されるリソースを簡単に取り出すことができます。「atom2html.cgi」は、AtomフィードのURLをパラメータとして、AtomのXMLファイルを取得します。「atom2html.cgi」のスクリプトの次の部分です。

LWPモジュールでAtomフィードを取得する
use LWP;
my @header = (
    'UserAgent' => "libwww-perl/$LWP::VERSION",
);
my $browser = LWP::UserAgent->new;
 ..........

# ATOM URLとヘッダフィールドにUserAgent値を指定して ATOM ファイルを取得
my $res;
unless($res = $browser->get($getfile,@header)){
    print "<p>LWP::UserAgentのget関数がundef値を返しました。
</p></BODY></HTML>\n";
    exit;
}

my $content = $res->content;# UserAgentのget関数の戻り値

 LWP::Simpleというモジュールを使うと、引数はURLだけになり、もっと簡単になりますが、Webサイトの設定によっては、リクエストのヘッダフィールドにUserAgentの値を含めないと応答してくれない場合があります。私の経験では、libwww-perlのバージョンを送る方法で、問題なく応答が得られるようになりました。本稿とは関係ありませんが、LWP::Simpleで応答が得られない場合はこの方法を試してみるとよいでしょう。

Encodeモジュール

 AtomフィードのXML宣言のencoding属性を見て、UTF-8でなければ、該当のエンコーディングからutf8にfrom_to()を使って変換します。次の部分です。

エンコーディング情報をもとに、UTF-8文字コードに変換
use Encode qw(from_to);
 ..........

if($encoding ne "utf8"){
    from_to($content,$encoding,"utf8");
}

atom2html.cgi

 以下に、CGI出力画面とスクリプトを示します。

「atom2html.cgi」出力画面
「atom2html.cgi」出力画面
「atom2html.cgi」:AtomフィードURLをパラメータとして、内容を解析、HTMLに変換表示するCGI
#!/Perl5.8/bin/perl.exe
use strict;
use warnings;
use CGI qw(:cgi);
use Encode qw(from_to);
use LWP;
my @header = (
    'UserAgent' => "libwww-perl/$LWP::VERSION",
);
my $browser = LWP::UserAgent->new;

# 環境変数の取得
# ローカル HTTP サーバーのドキュメントルートのフルパス
my $docroot = $ENV{'DOCROOT'};
#$ENV{'CGIDIR'};# ローカル HTTP サーバーの CGI 相対ディレクトリ
my $cgidir = "/cgi-bin/codezine";

my $getfile;# 取得ファイルのURL

# Atom URL データの取得
if(param('atom')){
    # CGI で URL を取得する
    $getfile = param('atom');
}elsif($ARGV[0]){
    # コマンドラインから URL を取得する
    $getfile = $ARGV[0];
}else{
    print "Atom URL データが存在しない。\n";exit;# 終了する
}

# CGI 最初の部分の出力
print <<HEADER;
Content-type: text/html; charset=UTF-8

<html>
<head>
    <title>ATOM Reader</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<h3>Atom Reader</h3>
HEADER

# Feed Protocol対応
$getfile =~ s/^feed:\/\/(http:\/\/.+)$/$1/i;
$getfile =~ s/^feed:(http:\/\/.+)$/$1/i;
$getfile =~ s/^feed(:\/\/.+)$/http$1/i;
$getfile =~ s/^feed:(.+)$/http:\/\/$1/i;

# ATOM URL の ATOM ファイルを取得
my $res;
unless($res = $browser->get($getfile,@header)){
    print "<p>LWP::UserAgentのget関数がundef値を返しました。
</p></BODY></HTML>\n";
    exit;
}

my $content = $res->content;# UserAgentのget関数の戻り値(ファイル内容)
my $encoding = "";    # Atomファイルのエンコーディング
my $enc;              # Atomファイルのエンコーディングの元の文字列データ

# エンコーディングの取得
if($content =~ /<\?xml +version=["']1.0["'] +encoding=
["']([^"']+)["'][^\?]*\?>/){
    $encoding = $1;$enc = $encoding;
    $encoding =~ s/^Shift_*JIS$/sjis/i; # シフト JIS
    $encoding =~ s/^euc-*j*p*$/euc-jp/i;# EUC-JP
    $encoding =~ s/^utf-*8$/utf8/i;     # UTF-8
    $encoding =~ s/^ISO-8859-1$/ascii/i;# ISO-8859-1
    $encoding =~ s/^us-ascii$/ascii/i;  # US-ASCII
    $encoding =~ s/^ISO-2022-JP$/jis/i; # JIS
}

if($encoding eq ""){
    # エンコーディング情報のないものは、UTF-8 とする
    # 実際のRSSファイルにはXMLを宣言する行、<?xml...?>自体がないものもある
    $encoding = "utf8";$enc = "None";
}

# Unicode文字16進表記をUnicode文字に変換
$content =~ s/&#x(\w{4});/chr(hex($1))/eg;

# エンコーディング情報をもとに、UTF-8文字コードに変換
if($encoding ne "utf8"){
    from_to($content,$encoding,"utf8");
}

# Atom ファイルの解析
# 解析用の前処理: 文字列データから空白を取り除き、改行文字を削除する
$content =~ s/(>)[\r\n]+[ \t]*/$1/g;

# 終了タグの後に改行を入れる
$content =~ s/(<\/[^>]+>)/$1\n/g;

# 主要素の終了タグの前に改行を入れる
$content =~ s/(<\/feed>)/\n$1/g;
$content =~ s/(<\/entry>)/\n$1/g;
$content =~ s/(<\/author>)/\n$1/g;
$content =~ s/(<\/info>)/\n$1/g;
$content =~ s/(<\/content>)/\n$1/g;
$content =~ s/(<\/tagline>)/\n$1/g;
$content =~ s/(<\/title>)/\n$1/g;
$content =~ s/(<\/summary>)/\n$1/g;
$content =~ s/(<\/subtitle>)/\n$1/g;
$content =~ s/(<\/pre>)/\n$1/ig;# preタグの処理

# 主要素の開始タグの後に改行を入れ、要素タグのみの行を作る
$content =~ s/(<feed[^>]*>)/$1\n/g;
$content =~ s/(<entry[^>]*>)/$1\n/g;
$content =~ s/(<author[^>]*>)/$1\n/g;
$content =~ s/(<link[^>]*>)/$1\n/g;
$content =~ s/(<info[^>]*>)/$1\n/g;
$content =~ s/(<content[^>]*>)/$1\n/g;
$content =~ s/(<tagline[^>]*>)/$1\n/g;
$content =~ s/(<title[^>]*>)/$1\n/g;
$content =~ s/(<summary[^>]*>)/$1\n/g;
$content =~ s/(<subtitle[^>]*>)/$1\n/g;
$content =~ s/(<pre>)/$1\n/ig;# preタグの処理

# CDATAセクションの処理
$content =~ s/(<!\[CDATA\[)/\n$1\n/g;
$content =~ s/(\]\]>)/\n$1\n/g;

# データ文字列を改行で分割して、行単位のデータで配列に格納する
my @content = split(/\n+/, $content);

my $ver = "1.0";# デフォルトAtomバージョン
my $line;       # 行単位文字列データ
my $cdatasw = 0;# cdata セクションのフラグ
my $cdexist = 0;# cdata セクションの第2フラグ
my $cdata;      # cdataセクションデータ
my $fsw = 0;    # feed要素のフラグ
my $isw = 0;    # info要素のフラグ
my $esw = 0;    # entry要素のフラグ
my $type;       # content要素のtype属性
my $mode;       # content要素のmode属性
my $consw = 0;  # content要素のフラグ
my $tsw = 0;    # tagline要素のフラグ
my $tisw = 0;   # title要素のフラグ
my $susw = 0;   # summary要素のフラグ
my $stsw = 0;   # subtitle要素のフラグ
my $presw = 0;  # preタグ要素開始のフラグ
my $preexist = 0;# preタグの第2フラグ

# feed要素の副要素名をキーとする要素値の連想配列と初期化
my %feed = (
            'link', "",        # 0.3, 1.0
            'title', "",       # 0.3, 1.0
            'tagline', "",     # 0.3
            'modified', "",    # 0.3
            'info', "",        # 0.3, 1.0
            'subtitle', "",    # 1.0
            'updated', "",     # 1.0
            'category', "",    # 1.0
            'id', "",          # 0.3, 1.0
            'contributor', "", # 1.0
            'generator', "",   # 0.3, 1.0
            'icon', "",        # 1.0
            'logo', "",        # 1.0
            'rights', "",      # 1.0
            'email', "",       # 1.0
            'uri', "",         # 1.0
);
# entry要素の副要素名をキーとする要素値の連想配列と初期化
my %entry = (
            'issued', "",    # 0.3
            'published', "", # 1.0
            'link', "",      # 0.3, 1.0
            'title', "",     # 0.3, 1.0
            'content', "",   # 0.3, 1.0
            'name', "",      # 0.3, 1.0
            'modified', "",  # 0.3
            'created', "",   # 0.3
            'id', "",        # 0.3, 1.0
            'updated', "",   # 1.0
            'summary', "",   # 1.0
            'category', "",  # 0.3, 1.0
            'contributor', "", # 1.0
            'rights', "",      # 1.0
            'source', "",      # 1.0
            'draft', "",       # 0.3
            'email', "",       # 1.0
            'uri', "",         # 1.0
);
my $buffer;   # バッファ変数
my $linkattr; # link要素の属性部文字列
my $attr;     # link要素の属性名
my @attrs;    # link要素の属性名配列
my %lattr;    # link要素の属性名をキーとする属性値の連想配列
my @entries;  # %entryの配列要素をタブでつなぎ、entry単位で格納した配列
my $val;      # %entryの配列要素名
my %vals;     # %entryの配列要素の有無を判定するための連想配列;

# データを行単位で処理する。
foreach $line (@content){
    # Atom バージョンの判定
    if($line =~ /<feed.+?version=["']0\.3["']/){
        $ver = "0.3";
    }
    # CDATA セクションの処理 <![CDATA[・・・]]> の除去と実体参照エンコード
    if($line =~ /^<!\[CDATA\[$/){
        $cdatasw = 1;$cdexist = 1;next;
    }
    if($cdatasw == 1 && $line !~ /^\]\]>$/){
        $cdata .= $line . "\n";# 元々あった改行の付加
        next;
    }elsif($cdatasw == 1 && $line =~ /^\]\]>$/){
        $line = &entities_encode($cdata);
        $cdatasw = 0;$cdata = "";
    }
# feed、info、entry、content、tagline、
# title、summary、subtitle 要素開始の判定
    if($line =~ /<feed/){
        $fsw = 1;
    }elsif($line =~ /<info/){
        $isw = 1;next;
    }elsif($line =~ /<entry/){
        $esw = 1;$fsw = 0;# feed 要素終了の判定
    }elsif($line =~ /<content/){
        if($line =~ /type=['"]([^'"]+)['"]/){
            $type = $1;# type属性の取得
        }
        if($line =~ /mode=["']([^"']+)["']/){
            $mode = $1;# 0.3
        }
        $consw = 1;next;
    }elsif($line =~ /<tagline/){
        next;
    }elsif($line =~ /<title/){
        $tisw = 1;next;
    }elsif($line =~ /<summary/){
        $susw = 1;next;
    }elsif($line =~ /<subtitle/){
        $stsw = 1;next;
    }
    # subtitle 要素終了の判定
    if($stsw == 1 && $line =~ /<\/subtitle>/){
        if($fsw == 1){
            $feed{'subtitle'} = $buffer;$stsw = 0;$buffer = "";next;
        }elsif($esw == 1){
            $entry{'subtitle'} = $buffer;$stsw = 0;$buffer = "";next;
        }
    }
    # subtitle 要素の格納
    if($stsw == 1){
        if($line =~ /[<>]+/){
            $buffer .= &entities_encode($line);
        }else{
            $buffer .= $line;
        }
        next;
    }
    # summary 要素終了の判定
    if($susw == 1 && $line =~ /<\/summary>/){
        if($fsw == 1){
            $feed{'summary'} = $buffer;$susw = 0;$buffer = "";next;
        }elsif($esw == 1){
            $entry{'summary'} = $buffer;$susw = 0;$buffer = "";next;
        }
    }
    # summary 要素の格納
    if($susw == 1){
        if($line =~ /[<>]+/){
            $buffer .= &entities_encode($line);
        }else{
            $buffer .= $line;
        }
        next;
    }
    # title 要素終了の判定
    if($tisw == 1 && $line =~ /<\/title>/){
        if($fsw == 1){
            $feed{'title'} = $buffer;$tisw = 0;$buffer = "";next;
        }elsif($esw == 1){
            $entry{'title'} = $buffer;$tisw = 0;$buffer = "";next;
        }
    }
    # title 要素の格納
    if($tisw == 1){
        if($line =~ /[<>]+/){
            $buffer .= &entities_encode($line);
        }else{
            $buffer .= $line;
        }
        next;
    }
    # info 要素終了の判定
    if($isw == 1 && $line =~ /<\/info>/){
        $feed{'info'} = $buffer;$isw = 0;$buffer = "";next;
    }
    # info 要素の格納
    if($isw == 1){
        if($line =~ /[<>]+/){
            $buffer .= &entities_encode($line);
        }else{
            $buffer .= $line;
        }
        next;
    }
    # content 要素終了の判定
    if($consw == 1 && $line =~ /<\/content>/){
        $entry{'content'} = $buffer;$consw = 0;$buffer = "";next;
    }
    # content 要素の格納
    if($consw == 1){
        # preタグ処理
        if($line =~ /<pre>/i){
            $presw = 1;
        }
        if($line =~ /<\/pre>/i){
            $line .= "\n";# 元々あった改行の付加
            $presw = 0;$preexist = 1;
        }
        if($presw == 1){
            $line .= "\n";# 元々あった改行の付加
        }
        $buffer .= $line;
        next;
    }
    # link 要素の格納
    if($line =~ /<link +([^ ].+?[^ ]) *\/>/){
        $linkattr = $1;
        @attrs = ($linkattr =~ /([^ =]+=["'][^"']+["'])/g);
        foreach $attr (@attrs){
            if($attr =~ /^([^=]+)=["']([^"']+)["']$/){
                $lattr{$1} = $2;
            }
        }
        # feed要素の場合の処理
        if($fsw == 1){
            if($lattr{'type'}){
                if($lattr{'type'} eq "text/html"){
                    $feed{'link'} = $lattr{'href'};
                    $feed{'title'} = $lattr{'title'} unless($feed{'title'});
                }elsif($lattr{'type'} eq "application/atom+xml"){
                    $feed{'link'} = $lattr{'href'};
                    $feed{'title'} = $lattr{'title'} unless($feed{'title'});
                }
            }else{
                $feed{'link'} = $lattr{'href'};
                $feed{'title'} = $lattr{'title'} unless($feed{'title'});
            }
        }
        # entry要素の場合の処理
        if($esw == 1){
            if($lattr{'type'}){
                if($lattr{'type'} eq "text/html"){
                    $entry{'link'} = $lattr{'href'};
                    $entry{'title'} = $lattr{'title'} unless($entry{'title'});
                }elsif($lattr{'type'} eq "application/atom+xml"){
                    $entry{'link'} = $lattr{'href'};
                    $entry{'title'} = $lattr{'title'} unless($entry{'title'});
                }
            }else{
                $entry{'link'} = $lattr{'href'};
                $entry{'title'} = $lattr{'title'} unless($entry{'title'});
            }
        }
        @attrs = ();%lattr = ();$linkattr = "";$attr = "";
        next;
    }
    # 属性を持たない要素データを要素毎に要素名をキーとして取得
    if($line =~ /<[^>]+>(.+?)<\/([^>]+)>/){
        # feed 要素の格納
        if($fsw == 1){
            $feed{$2} = $1;
        }
        # entry 要素の格納
        if($esw == 1){
            $entry{$2} = $1;
        }
    }
    # entry 要素終了の判定と entry ごとのデータの格納
    if($esw == 1 && $line =~ /<\/entry>/){
        if($entry{'issued'}){
            $entry{'published'} = $entry{'issued'};
        }
        # 表示要素追加時チェック部(1)
        push(@entries, join("\@\@\@",$entry{'published'},$entry{'link'},
$entry{'title'},$entry{'content'},$entry{'name'},$entry{'modified'},
$entry{'created'},$entry{'id'},$entry{'updated'},$entry{'summary'},
$entry{'category'}));
        $esw = 0;
        foreach $val (keys (%entry)){
            if($entry{$val}){
                $vals{$val} = 1;
            }
        }
        # 表示要素追加時チェック部(2)
        %entry = (
            'issued', "",
            'published', "",
            'link', "",
            'title', "",
            'content', "",
            'name', "",
            'modified', "",
            'created', "",
            'id', "",
            'updated', "",
            'summary', "",
            'category', "",
        );
    }
}

# feed 要素の info 要素を実体参照デコードしておく
$feed{'info'} = &entities_decode($feed{'info'});

if($ver eq "0.3"){
    print "<dl><dt><a href=\"",$feed{'link'},"\" target=\"main\">",
 $feed{'title'}, "</a></dt><dd><p>",$feed{'tagline'}," [",
$feed{'modified'},"]</p><p>",$feed{'info'},"</p></dd></dl>\n";
}else{
    print "<dl><dt><a href=\"",$feed{'link'},"\" target=\"main\">",
 $feed{'title'}, "</a></dt><dd><p>",$feed{'subtitle'}," [",
$feed{'updated'},"]</p><p>",$feed{'summary'},"</p></dd></dl>\n";
}

# 表示要素追加時チェック部(3)
my $entry;     # entry要素の副要素をタブでつないだ文字列データ
my $published; # entry要素のpublished要素の値
my $link;      # entry要素のlink要素の値
my $title;     # entry要素のtitle要素の値
my $econtent;  # entry要素のcontent要素の値
my $name;      # entry要素のname要素の値
my $modified;  # entry要素のmodified要素の値
my $created;   # entry要素のcreated要素の値
my $id;        # entry要素のid要素の値
my $updated;   # entry要素のupdated要素の値
my $summary;   # entry要素のsummary要素の値
my $category;  # entry要素のcategory要素の値
my $titlelink; # title要素にリンクがある場合の処理文字列
my $linkhead;  # title要素がない場合に作成するタイトル文字列

# entry 要素データを HTML 表に変換して出力
print "<table border=0>\n";
# 表示要素追加時チェック部(4)
print "<tr bgcolor=\"#eeeeee\"><th>タイトル</th><th>記事</th>
<th>日付/更新</th><th>著者</th><th>カテゴリ</th></tr>\n";

# entry ごとに表示要素を処理する
foreach $entry (@entries){
    # 表示要素追加時チェック部(5)
    ($published,$link,$title,$econtent,$name,$modified,$created,
$id,$updated,$summary,$category) = split(/\@\@\@/,$entry);
    print "<tr bgcolor=\"#eeeeee\">";
    # タイトルとリンクの処理
    if($title){
        $title = &entities_decode($title);
        # タイトルにリンクがある場合は、別にリンクする
        if($title =~ s/(<a.+?)>(.+?)(<\/a>)/$2/i){
            $titlelink = " -> $1 target=\"main\">LINK$3";
        }else{
            $titlelink = "";
        }
        # ATOM の entry 主要素の link 要素の場合
        if($link){
            print "<td><font color=\"#000000\"><a href=\"", $link,
 "\" target=\"main\">", $title, "</a></font>$titlelink</td>";
        }else{
            print "<td><font color=\"#000000\">$title</font>$titlelink</td>";
        }
    }else{
        # タイトルがない場合は、リンク文字列にリンクしてタイトルに表示する
        # ATOM の entry 主要素の link 要素の場合
        if($link){
            if($link !~ /http:/){ # PATH のみの URL の場合、
                ($linkhead = $feed{'link'}) =~ s/^(http:\/\/.+?\/)
(~\w+?\/)*.*$/$1$2/; # feed のリンクから BASE URL を取り出し、
                $link = $linkhead . $link; # PATH に URL を付加する。
            }
            print "<td><font color=\"#000000\"><a href=\"", $link,
 "\" target=\"main\">", $link, "</a></font></td>";
        }else{
            print "<td><br></td>\n";
        }
    }
    # entry 記事相当要素の出力
    if($econtent){
        if(($econtent =~ /(&[a-z0-9]{2};|&#\d+;)/ && $preexist == 0) or
 ($cdexist == 1) or ($mode eq 'escaped')){
            $econtent = &entities_decode($econtent);
            # 実体参照への変換が二重に行われている場合の対応
            if($econtent =~ /(&[a-z0-9]{2};|&#\d+;)/){
                $econtent = &entities_decode($econtent);
            }
        }
        # HTML 表の表示が乱れるため、<p>タグは除去、
        # </p>タグを<br>に置換して出力
        $econtent =~ s/<p[^r>]*>//ig;
        $econtent =~ s/<\/p>/<br \/>/ig;
        print "<td>",$econtent,"</td>\n";$econtent = "";
    }elsif($summary){
        if(($summary =~ /(&[a-z0-9]{2};|&#\d+;)/ && $preexist == 0) or
 ($cdexist == 1) or ($mode eq 'escaped')){
            $summary = &entities_decode($summary);
            # 実体参照への変換が二重に行われている場合の対応
            if($summary =~ /(&[a-z0-9]{2};|&#\d+;)/){
                $summary = &entities_decode($summary);
            }
        }
        # HTML 表の表示が乱れるため、<p>タグは除去、</p>タグを<br>に置換して出力
        $summary =~ s/<p[^r>]*>//ig;
        $summary =~ s/<\/p>/<br \/>/ig;
        print "<td>",$summary,"</td>\n";$summary = "";
    }else{
        print "<td><br /></td>\n";
    }
    # entry 日付要素
    if($published){
        print "<td>",$published;
        if($updated){
            print "<br />更新: ",$updated,"</td>\n";
        }else{
            print "</td>\n";
        }
    }else{
        if($updated){
            print "<td>更新: ",$updated,"</td>\n";
        }else{
            print "<td><br /></td>\n";
        }
    }
    # entry 製作者要素
    if($name){
        print "<td>",$name,"</td>\n";
    }else{
        print "<td><br /></td>\n";
    }
    # entry category要素
    if($category){
        print "<td>",$category,"</td>\n";
    }else{
        print "<td><br /></td>\n";
    }
    # entry id 要素
#    if($id){
#        print "<td>",$id,"</td>\n";
#    }else{
#        print "<td><br /></td>\n";
#    }
    print "</tr>\n";
}
print "</table>\n";# Atom ファイルの記事内容表出力終了
print "<hr />\n<ul>\n";# Atom entry副要素リスト出力
foreach (keys (%vals)){
    print "<li>",$_,"</li>\n";
}
print "</ul></body>\n</html>\n";
# HTML 出力終了

# サブルーチン

# マークアップ記号の実体参照デコード
# 文字実体参照と数値実体参照の両方に対応
sub entities_decode{
    my($str) = @_;
    $str =~ s/(<|&#60;)/</g;
    $str =~ s/(>|&#62;)/>/g;
    $str =~ s/('|&#39;)/'/g;
    $str =~ s/("|&#34;)/"/g;
    $str =~ s/(&|&#38;)/&/g;
    return $str;
}

# マークアップ記号の実体参照エンコード
sub entities_encode{
    my($str) = @_;
    $str =~ s/&/&/g;
    $str =~ s/</</g;
    $str =~ s/>/>/g;
    $str =~ s/'/'/g;
    $str =~ s/"/"/g;
    return $str;
}

次のページ
まとめ

修正履歴

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
PerlでデスクトップCGI連載記事一覧

もっと読む

この記事の著者

jscripter(ジェイスクリプター)

NIFTY SERVEのFGALEL(エントリーラボ)、FGALTS(テキスト&スクリプト)に参画し、FGALTSでシスオペを務める。現在、TSNETメンバー。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/397 2006/10/10 12:17

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング