まずは、RSS 1.0 を対象に RSS リーダーを作ってみることにします。
一般的に RSS 1.0 の rdf ファイルは、以下のような形式をしています。
(実際には、もっといろんなバリエーションが可能なのですが、ここでは簡単のために最も一般的な例を示しています。)
<?xml version="1.0" encoding="UTF-8"?> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:admin="http://webns.net/mvcb/" xmlns="http://purl.org/rss/1.0/"> <channel rdf:about="RSS の対象となる Web ページの URL"> <title>RSS の対象となる Web ページのタイトル</title> <link>RSS の対象となる Web ページの URL</link> <description>RSS の対象となる Web ページの概要</description> <dc:language>言語(日本語の場合は ja)</dc:language> <dc:creator>RSS の対象となる Web ページの著作者</dc:creator> <dc:date>RSS の対象となる Web ページの最終更新日時</dc:date> <admin:generatorAgent rdf:resource="RSS生成に使用したツールの URL" /> <items> <rdf:Seq> <rdf:li rdf:resource="RSS 生成対象の 最初 の記事の URL" /> <rdf:li rdf:resource="RSS 生成対象の 2番目 の記事の URL" /> ・・・・・・・・・・・・(略)・・・・・・・・・・・・ <rdf:li rdf:resource="RSS 生成対象の n番目 の記事の URL" /> </rdf:Seq> </items> <item rdf:about="RSS 生成対象の 最初 の記事の URL"> <title>RSS 生成対象の 最初 の記事のタイトル</title> <link>RSS 生成対象の 最初 の記事の URL</link> <description>RSS 生成対象の 最初 の記事の概要</description> <dc:subject>RSS 生成対象の 最初 の記事の分類</dc:subject> <dc:creator>RSS 生成対象の 最初 の記事の著作者</dc:creator> <dc:date>RSS 生成対象の 最初 の記事の作成日時</dc:date> </item> <item rdf:about="RSS 生成対象の 2番目 の記事の URL"> <title>RSS 生成対象の 2番目 の記事のタイトル</title> <link>RSS 生成対象の 2番目 の記事の URL</link> <description>RSS 生成対象の 2番目 の記事の概要</description> <dc:subject>RSS 生成対象の 2番目 の記事の分類</dc:subject> <dc:creator>RSS 生成対象の 2番目 の記事の著作者</dc:creator> <dc:date>RSS 生成対象の 2番目 の記事の作成日時</dc:date> </item> ・・・・・・・・・・・・(略)・・・・・・・・・・・・ <item rdf:about="RSS 生成対象の n番目 の記事の URL"> <title>RSS 生成対象の n番目 の記事のタイトル</title> <link>RSS 生成対象の n番目 の記事の URL</link> <description>RSS 生成対象の n番目 の記事の概要</description> <dc:subject>RSS 生成対象の n番目 の記事の分類</dc:subject> <dc:creator>RSS 生成対象の n番目 の記事の著作者</dc:creator> <dc:date>RSS 生成対象の n番目 の記事の作成日時</dc:date> </item> </channel> </rdf:RDF>
とりあえず、方針としては、RSS 1.0 形式の rdf ファイルを入力とし、html に変換して出力するコンソールアプリケーションを作ることにします。入力ファイルに
xmlns="http://purl.org/rss/1.0/"
という文字列が含まれていたら、RSS 1.0 と見なすことにします。
大雑把にコードを書くと、以下のような感じになります。
int readRSS( FILE *input, /* 入力 RSS ファイル */ FILE *output /* 出力 htmlファイル */ ) { char buf[BUFSIZ], *p, *q, *r; char encoding[80]; int version = VER_UNDEFINED; int ret = 0; while ( ( p = fgets( buf, BUFSIZ - 1, input ) ) != NULL ) { while ( (*p == ' ') || (*p == '\t') ) p++; /* encoding を判定 */ if ( !strncmp( p, "<?xml ", 6 ) ) { if ( ( q = strstr( p, "encoding=" ) ) != NULL ) { r = strchr( q + 10, '\"' ); strncpy( encoding, q + 10, r - (q + 10) ); encoding[r - (q + 10)] = '\0'; } continue; } /* RSS の版数を判定し、対応する RSS リーダーを呼び出す */ if ( !strncmp( p, "xmlns=\"http://purl.org/rss/1.0/\"", 32 ) ) { version = VER_RSS_1_00; ret = readRSS_1_00( input, output, encoding ); break; } } return ( ret ); }
で、RSS 1.0 のリーダー readRSS_1_00() は、以下のような感じでしょうか?
int readRSS_1_00( FILE *input, FILE *output, const char *encoding ) FILE *input, /* 入力 RSS ファイル */ FILE *output, /* 出力 htmlファイル */ const char *encoding /* エンコード */ ) { char buf[BUFSIZ], *p, *q; int ret = 0; char title[BUFSIZ]; char link[BUFSIZ]; char description[BUFSIZ]; char language[BUFSIZ]; char subject[BUFSIZ]; char date[BUFSIZ]; int cnt = 0; language[0] = '\0'; while ( ( p = fgets( buf, BUFSIZ - 1, input ) ) != NULL ) { while ( (*p == ' ') || (*p == '\t') ) p++; 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, '<' ); strncpy( description, p + 13, q - (p + 13) ); description[q - (p + 13)] = '\0'; } else if ( !strncmp( p, "<dc:language>", 13 ) ) { q = strchr( p + 13, '<' ); strncpy( language, p + 13, q - (p + 13) ); language[q - (p + 13)] = '\0'; } else if ( !strncmp( p, "</items>", 8 ) ) break; } fprintf( output, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD " "HTML 4.01 Transitional//EN\"gt;\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", encoding ); fprintf( output, "<title>%s</title>\n", title ); fprintf( output, "</head>\n\n<body>\n" ); 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 rdf:about=\"", 17 ) ) { cnt++; if ( cnt == 1 ) fputs( "<ul>\n", output ); title[0] = '\0'; link[0] = '\0'; description[0] = '\0'; subject[0] = '\0'; date[0] = '\0'; 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, '<' ); strncpy( description, p + 13, q - (p + 13) ); description[q - (p + 13)] = '\0'; } else if ( !strncmp( p, "<dc:subject>", 12 ) ) { q = strchr( p + 12, '<' ); strncpy( subject, p + 12, q - (p + 12) ); subject[q - (p + 12)] = '\0'; } else if ( !strncmp( p, "<dc:date>", 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 ); }
ちょっと安直なコードですが、直感的にはわかりやすいと思います。 ソースがある程度書き進んだところで、 リファクタリングに挑戦することにしましょう。
なお、dateString() は RDF の datetime型 の日時表現
(例: 2003-12-16T13:00:00+9:00)
を 2003年12月16日 13時 0分 0秒 +9:00 のような形式に変換する関数とします
(別途、用意します)。
また、categoryString()
の方は「分類」を表示するときの見出しを
encoding にしたがった文字コードで出力する関数とします。
文字コードの変換は GNU の iconv ライブラリ を使えば、わざわざ、新規に作る必要はありません。 すでにあるものは使え、ってことです。
今回、作成した関数のプロトタイプ宣言をヘッダファイルに書いておきます。
/* * readRSS * RSS ファイルを元に html ファイルを生成する * written by H.Tsujimura 15 Dec 2003 * * History: * $Log: readRSS.h $ * Revision 1.1 2003/12/15 10:50:51 tsujimura543 * Initial revision * */ #ifndef __READRSS_H__ #define __READRSS_H__ #ifdef __READRSS_C__ #ifndef lint static char *rcs_id_header = "$Header: C:/user/local/src/readRSS/RCS/readRSS.h 1.1 2003/12/15 10:50:51 tsujimura543 Exp tsujimura543 $"; #endif #endif /* RSS version */ #define VER_UNDEFINED 0 #define VER_RSS_1_00 0x01 int readRSS( FILE *input, FILE *output ); int readRSS_1_00( FILE *input, FILE *output, const char *encoding ); char *dateString( const char *date, const char *encoding ); char *categoryString( const char *encoding ); #endif /* __READRSS_H__ */
次回は RSS 0.91 のリーダーを作ってみることにします。
(つづく)


