今回は、RSS 2.0 を扱うための処理を RSS リーダーに組み込んでみることにします。なぜ、RSS 0.92 や 0.93 ではなく、いきなり 2.0 かというと、0.92 や 0.93 は 2.0 のサブセットと見なせますので、2.0 のリーダを作れば、0.92 や 0.93 準拠の RSS ファイルにも自動的に対応したことになるからです。2.0 のリーダを使えば 0.91 もそのまま読めるんでは? という意見もあるかとは思いますが、0.91 と 0.92 以降で、微妙に取り扱いが変わった要素(elemnt)がありますので、今後のことを考えて、あえて 0.91 用のリーダは別に用意しました。
一般的に RSS 2.0 の xml ファイルは、以下のような形式をしています
(注意: 2.0 は rdf ではありません)。
RSS 1.0 と比べると記述できる情報の種類が大幅に増えています。また、RSS 0.91 に存在した項目数(最大15項目)や文字数(1項目最大500バイト)に関する制限が撤廃されています。
また、RSS 1.0 では日時は ISO8601 形式ですが、RSS 0.92, 0.93, 2.0 では RFC822 形式です。
(実際には、もっといろんなバリエーションが可能なのですが、ここでは簡単のために最も一般的な例を示しています。)
<?xml version="1.0" encoding="ISO-8859-1" ?> <rss version="2.0"> <channel> <title>RSS の対象となる Web ページのタイトル</title> <link>RSS の対象となる Web ページの URL</link> <description>RSS の対象となる Web ページの概要</description> <language>言語(日本語の場合は ja-jp)</language> <copyright>RSS の対象となる Web ページに関する著作権に関する情報</copyright> <pubDate>RSS の対象となる Web ページの最終更新日時</pubDate> <lastBuildDate>RSS ファイルの生成日時</lastBuildDate> <docs>RSS ファイルの URL</docs> <generator>RSS ファイル生成ツール名</generator> <managingEditor>RSS の対象となる Web ページの編集者のemailアドレス</managingEditor> <webMaster>RSS の対象となる Web ページの管理者のemailアドレス</webMaster> <image> <url>当該Web ページ、もしくは主宰者にちなむ画像のURL</url> <link&Web ページの URLgt;</link> <title>Web ページのタイトルもしくは主宰者の名前、など</title> </image> <item> <title>RSS 生成対象の 最初 の記事の 題名</title> <link>RSS 生成対象の 最初 の記事の URL</link> <description>RSS 生成対象の 最初 の記事の 概要</description> <pubDate>RSS 生成対象の 最初 の記事の 最終更新日時</pubDate> <guid>RSS 生成対象の 最初 の記事を特定するための 識別子 または 固定リンク</guid> </item> <item> <title>RSS 生成対象の 2番目 の記事の 題名</title> <link>RSS 生成対象の 2番目 の記事の URL</link> <description>RSS 生成対象の 2番目 の記事の 概要</description> <pubDate>RSS 生成対象の 2番目 の記事の 最終更新日時</pubDate> <guid>RSS 生成対象の 2番目 の記事を特定するための 識別子 または 固定リンク</guid> </item> ・・・・・・・・・・・・(略)・・・・・・・・・・・・ <item> <title>RSS 生成対象の n番目 の記事の 題名</title> <link>RSS 生成対象の n番目 の記事の URL</link> <description>RSS 生成対象の n番目 の記事の 概要</description> <pubDate>RSS 生成対象の n番目 の記事の 最終更新日時</pubDate> <guid>RSS 生成対象の n番目 の記事を特定するための 識別子 または 固定リンク</guid> </item> </channel> </rss>
入力ファイルに
<rss version="0.92">
<rss version="0.93">
<rss version="2.0">
という文字列のいずれかが含まれていたら、RSS 2.0 用のリーダで処理することにします。
RSS 2.0 のリーダー readRSS_2_00() を、これまた やっつけ (^^; で作ると、 以下のようになります。
int readRSS_2_00( FILE *input, FILE *output, int mode, const char *encoding ) { char buf[BUFSIZ], *p, *q, *r; int ret = 0; char title[BUFSIZ]; char link[BUFSIZ]; char description[BUFSIZ]; char language[BUFSIZ]; char subject[BUFSIZ]; char date[BUFSIZ]; char imageUrl[BUFSIZ]; char imageLink[BUFSIZ]; char imageTitle[BUFSIZ]; int cnt = 0; int image = 0; language[0] = '\0'; imageUrl[0] = '\0'; imageLink[0] = '\0'; imageTitle[0] = '\0'; while ( ( p = fgets( buf, BUFSIZ - 1, input ) ) != NULL ) { while ( (*p == ' ') || (*p == '\t') ) p++; if ( image ) { if ( !strncmp( p, "<url>", 5 ) ) { q = strchr( p + 5, '<' ); if ( q ) { strncpy( imageUrl, p + 5, q - (p + 5) ); imageUrl[q - (p + 5)] = '\0'; } continue; } if ( !strncmp( p, "<link>", 6 ) ) { q = strchr( p + 6, '<' ); if ( q ) { strncpy( imageLink, p + 6, q - (p + 6) ); imageLink[q - (p + 6)] = '\0'; } continue; } if ( !strncmp( p, "<title>", 7 ) ) { q = strchr( p + 7, '<' ); if ( q ) { strncpy( imageTitle, p + 7, q - (p + 7) ); imageTitle[q - (p + 7)] = '\0'; } continue; } } if ( !strncmp( p, "<title>", 7 ) ) { q = strchr( p + 7, '<' ); if ( q ) { strncpy( title, p + 7, q - (p + 7) ); title[q - (p + 7)] = '\0'; } else { strcpy( title, p + 13 ); while ( ( p = fgets( buf, BUFSIZ - 1, input ) ) != NULL ) { while ( (*p == ' ') || (*p == '\t') ) p++; strcat( title, " " ); strcat( title, p ); if ( ( q = strchr( title, '<' ) ) != NULL ) { *q = '\0'; break; } } } } else if ( !strncmp( p, "<link>", 6 ) ) { q = strchr( p + 6, '<' ); if ( q ) { strncpy( link, p + 6, q - (p + 6) ); link[q - (p + 6)] = '\0'; } } else if ( !strncmp( p, "<description>", 13 ) ) { q = strchr( p + 13, '<' ); if ( q ) { strncpy( description, p + 13, q - (p + 13) ); description[q - (p + 13)] = '\0'; } else { strcpy( description, p + 13 ); while ( ( p = fgets( buf, BUFSIZ - 1, input ) ) != NULL ) { while ( (*p == ' ') || (*p == '\t') ) p++; strcat( description, " " ); strcat( description, p ); if ( ( q = strchr( description, '<' ) ) != NULL ) { *q = '\0'; break; } } } if ( description[0] && (strchr(description, '&') != NULL) ) { // 実体参照が含まれる場合 p = description; while ( ( ( q = strchr( p, '&' ) ) != NULL ) && ( ( r = strchr( q, ';' ) ) != NULL ) ) { if ( !strncmp( q + 1, "lt", 2 ) ) { *q = '<'; strcpy( q + 1, r + 1 ); } else if ( !strncmp( q + 1, "gt", 2 ) ) { *q = '>'; strcpy( q + 1, r + 1 ); } else if ( !strncmp( q + 1, "amp", 3 ) ) { *q = '&'; strcpy( q + 1, r + 1 ); } else if ( !strncmp( q + 1, "nbsp", 4 ) ) { *q = ' '; strcpy( q + 1, r + 1 ); } else { strcpy( q, r + 1 ); } } } } else if ( !strncmp( p, "<language>", 10 ) ) { q = strchr( p + 10, '<' ); if ( q ) { strncpy( language, p + 10, q - (p + 10) ); language[q - (p + 10)] = '\0'; } } else if ( !strncmp( p, "<image>", 7 ) ) image = 1; else if ( !strncmp( p, "</image>", 8 ) ) image = 0; else if ( !strncmp( p, "<item>", 6 ) ) break; } if ( !(language[0]) ) { /* if ( !strcmp( encoding, "ISO-8859-1" ) || !strcmp( encoding, "ISO-2022-JP" ) || !strcmp( encoding, "Shift_JIS" ) || !strcmp( encoding, "EUC-JP" ) || !strcmp( encoding, "UTF-8" ) ) strcpy( language, "ja_jp" ); else strcpy( language, "en_us" ); */ strcpy( language, "ja_jp" ); } fprintf( output, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD " "HTML 4.01 Transitional//EN\">\n" ); if ( language[0] ) fprintf( output, "<html lang=\"%s\">\n<head>\n", language ); else fprintf( output, "<html>\n<head>\n" ); fprintf( output, "<meta http-equiv=\"Content-Type\" content=\"text/html; " "charset=%s\">\n", !strcmp( encoding, "ISO-8859-1" ) ? "Shift_JIS" : encoding ); fprintf( output, "<title>%s</title>\n", title ); fprintf( output, "</head>\n\n<body>\n" ); if ( imageUrl[0] ) fprintf( output, "<a href=\"%s\"><img src=\"%s\" alt=\"%s\" " "border=\"0\" align=\"right\"></a>\n", imageLink[0] ? imageLink : link, imageUrl, imageTitle[0] ? imageTitle : title ); fprintf( output, "<h1><a href=\"%s\">%s</a></h1>\n", link, title ); fprintf( output, "<h2>%s</h2>\n", description ); while ( ( p = fgets( buf, BUFSIZ - 1, input ) ) != NULL ) { while ( (*p == ' ') || (*p == '\t') ) p++; if ( !strncmp( p, "<item>", 6 ) || (cnt == 0) ) { cnt++; if ( cnt == 1 ) fputs( "<ul>\n", output ); title[0] = '\0'; link[0] = '\0'; description[0] = '\0'; subject[0] = '\0'; date[0] = '\0'; if ( cnt > 1 ) continue; } if ( cnt >= 1 ) { if ( !strncmp( p, "<title>", 7 ) ) { q = strchr( p + 7, '<' ); strncpy( title, p + 7, q - (p + 7) ); title[q - (p + 7)] = '\0'; } else if ( !strncmp( p, "<link>", 6 ) ) { q = strchr( p + 6, '<' ); strncpy( link, p + 6, q - (p + 6) ); link[q - (p + 6)] = '\0'; } else if ( !strncmp( p, "<description>", 13 ) ) { q = strchr( p + 13, '<' ); if ( q && !strncmpi( q, "<![CDATA[", 9 ) ) { p += 9; r = strchr( q, '>' ); if ( r ) { *r = '\0'; while ( *--r == ']' ) *r = '\0'; } q += 9; strcpy( description, q ); } else if ( q ) { strncpy( description, p + 13, q - (p + 13) ); description[q - (p + 13)] = '\0'; } if ( description[0] && (strchr(description, '&') != NULL) ) { // 実体参照が含まれる場合 p = description; while ( ( ( q = strchr( p, '&' ) ) != NULL ) && ( ( r = strchr( q, ';' ) ) != NULL ) ) { if ( !strncmp( q + 1, "lt", 2 ) ) { *q = '<'; strcpy( q + 1, r + 1 ); } else if ( !strncmp( q + 1, "gt", 2 ) ) { *q = '>'; strcpy( q + 1, r + 1 ); } else if ( !strncmp( q + 1, "amp", 3 ) ) { *q = '&'; strcpy( q + 1, r + 1 ); } else if ( !strncmp( q + 1, "nbsp", 4 ) ) { *q = ' '; strcpy( q + 1, r + 1 ); } else { strcpy( q, r + 1 ); } } } } else if ( !strncmp( p, "<category>", 10 ) ) { q = strchr( p + 10, '<' ); strncpy( subject, p + 10, q - (p + 10) ); subject[q - (p + 10)] = '\0'; } else if ( !strncmp( p, "<pubDate>", 9 ) ) { q = strchr( p + 9, '<' ); strncpy( date, p + 9, q - (p + 9) ); date[q - (p + 9)] = '\0'; } else if ( !strncmp( p, "</item>", 7 ) ) { if ( title[0] && link[0] && description[0] ) { fprintf( output, "<li><a href=\"%s\">%s</a><br>%s\n", link, title, description ); if ( subject[0] ) { fprintf( output, "<br>[%s%s]", categoryString( encoding ), subject ); if ( date[0] ) fprintf( output, " (%s)", dateString( date, encoding ) ); } else if ( date[0] ) fprintf( output, "<br>(%s)", dateString( date, encoding ) ); fputs( "</li>\n", output ); title[0] = '\0'; link[0] = '\0'; description[0] = '\0'; subject[0] = '\0'; date[0] = '\0'; } } } } if ( cnt >= 1 ) fputs( "</ul>\n", output ); fprintf( output, "</body>\n</html>\n" ); return ( ret ); }
readRSS() の RSS 版数判定部分を以下のように書き換えて、 今回作った readRSS_2_00() を組み込めば OK。
/* RSS の版数を判定し、対応する RSS リーダーを呼び出す */
if ( !strncmp( p, "xmlns=\"http://purl.org/rss/1.0/\"", 32 ) ) {
version = VER_RSS_1_00;
ret = readRSS_1_00( input, output, mode, encoding );
break;
}
if ( !strncmp( p, "<rss version=\"0.91\">", 20 ) ) {
version = VER_RSS_0_91;
ret = readRSS_0_91( input, output, mode, encoding );
break;
}
if ( !strncmp( p, "<rss version=\"2.0\">", 19 ) ||
!strncmp( p, "<rss version=\"0.92\">", 20 ) ||
!strncmp( p, "<rss version=\"0.93\">", 20 ) ) {
version = VER_RSS_2_00;
ret = readRSS_2_00( input, output, mode, encoding );
break;
}
次回は Atom 0.3 のリーダーを作ってみることにします。
(つづく)


