2007年11月19日月曜日

茶筌chasen-2.4.2, Darts 0.31, UniDic1.3.5のインストール

最新版茶筌chasen-2.4.2をDarts 0.31、UniDic1.3.5と組み合わせてインストールしました。結構落とし穴があるようなので、参考のためにブログに記します。

Darts 0.31のインストール

wget http://chasen.org/~taku/software/darts/src/darts-0.31.tar.gz
tar xvzf darts-0.31.tar.gz
cd darts-0.31
./configure

*configure error: C++ prerrocessor "/lib/cpp" fails sanity check
などのエラーが出たら、C++のコンパイラがインストールされていないということなので、gcc-c++をインストールする。
yum install gcc-c++

そして作業再開。

make
make install

chasen-2.4.2のインストール

wget http://keihanna.dl.sourceforge.jp/chasen-legacy/26441/chasen-2.4.2.tar.gz
tar xvzf chasen-2.4.2.tar.gz
./configure --with-darts=/usr/local/include --with-libiconv=/usr/local

gcc(g++) verison 3.4.3以上でコンパイルエラーが起こるので、lib/dartsdic.cppの180行目を以下のように修正する。

修正前:(const char*)keys[size] = key.data();
修正後:keys[size] = (char*)key.data();

make
make install

uniDic-1.3.5のインストール


システムをEUCで統一しているので、http://www.tokuteicorpus.jpから個別ファイル->Linuxと進んで、UniDic-chasen-1.3.5_eucj.tar.gzをダウンロードする。

tar xvzf UniDic-chasen-1.3.5_eucj.tar.gz

/unidicというディレクトリに辞書ファイルや文法ファイルが入るので、それを/usr/local/lib/chasen/dicの下に移動する。

mv unidic /usr/local/lib/chasen/dic

これで/usr/local/lib/chasen/dicの下にipadicとunidicが並ぶ格好になる。

ipadicを使いたい時は、

chasen -r /usr/local/lib/chasen/dic/ipadic/chasenrc

unidicを使いたい時は、

chasen -r /usr/local/lib/chasen/dic/unidic/chasenrc

とすればよい。

2007年10月13日土曜日

[PHP] Finding the latest RSS item date

I’ve been podcasting for about a year. Recently, I thought of adding something like “(Last update: 12/10/06)” by a link to my podcast page. I also thought that it’d be neat to have this generated automatically (dynamically). After a few hours of experimentation in PHP, I came up with the following function.

I’ll list the whole code first, and then, go though it bit by bit later.

To use this code, put the following function definition in the header part of an HTMLfile.

<?php

function Find_Latest_From_RSS($filename) {
$str=file_get_contents($filename);
$p = xml_parser_create();
xml_parse_into_struct($p, $str, $vals, $index);
xml_parser_free($p);
for ($i=0; $i<count($vals); $i++)
if ($vals[$i][tag]=="ITEM") {
break;
}
}
$latest="January 1 1970 00:00:00 GMT"; // initialize
for (; $i<count($vals); $i++) { // start looking for PUBDATEs
if ($vals[$i][level]==4 && $vals[$i][tag]=="PUBDATE") {
$dateTime=strtotime($vals[$i][value]);
if ($latest<$dateTime) {
$latest=$dateTime;
}
}
}
return date("n/j/y",$latest);
}

?>

Then, in the body, where you want the date to appear, put

<?php echo Find_Latest_From_RSS("your_RSS_file"); ?>

For example,

(Lat update: <?php echo Find_Latest_From_RSS("your_RSS_file"); ?>)

will give you

(Last update: 12/11/06)

If you want to change the date format, modify this line:

date("n/j/y",$latest)

Breif documentation on the code follows. First, a function declaration.

function Find_Latest_From_RSS($filename) {

You pass the name of an RSS file as the argument. This means that it is possible to process multiple RSS files in a HTML file. This function returns the latest publication item date.

$str=file_get_contents($filename);

First, the entire RSS file is read in.

$p=xml_parser_create();
xml_parse_into_struct($p, $str, $vals, $index);
xml_parser_free($p);

These three lines go together. The first line creates a PHP xml parser object. The second line parses the RSS file content. The third line removes the parser object from the memory. The parsed data are put in $vals.

for ($i=0; $i<count($vals); $i++)
if ($vals[$i][tag]=="ITEM") {
break;
}
}

This loop looks for an <ITEM> tag and exits when it finds one. The purpose of this is to skip the materials that precede <ITEM> definitions.

$latest="January 1 1970 00:00:00 GMT";

In order to find the latest publication date, initialize $latest to some old date.

for (; $i<count($vals); $i++) {

Here’s another for loop. Notice that $i is not initialized. $i starts at the value from the last loop.

if ($vals[$i][level]==4 && $vals[$i][tag]=="PUBDATE") {

Look at each element in the array and if you find an element whose level is 4 and whose tag is PUBDATA, you’ll look at its value. What’s level 4? An item pubdate comes under <rss><channel><item>. Therefore, it’s level 4. There’s another pubdate at level 3, but it’s a publication date of the feed itself. Because the first loop skips over everything before the first item tag, we don’t really need to check on the level of each element here. This piece of code is there for the purpose of illustrating the data structure.

$dateTime=strtotime($vals[$i][value]);

strtotime is used here to convert a string representing a date into the date format. Once this is done, you can use comparison operators to compare between dates.

if ($latest<$dateTime) {
$latest=$dateTime;
}

A comparison is made here. Whenever a more recent date is found, it is stored in $latest.

This way, at the end of the loop, $latest should contain the latest item publication date.

return date("n/j/y",$latest);

The last thing to do is to convert the value in $latest into a string and return it as the return value of the whole function.

[PHP] RSSファイル内の最新itemの日付を探す

Podcastingを始めて1年ほどになります。最近、Podcastのページへのリンクのところに(12/10/06更新)などのように表示できたらいいなと思いました。さらに、更新日付が自動的に生成できたらいいなと考え、PHPのプログラミング開始。次のような関数を書くにいたりました。

まず全体を紹介して、後で少しずつ解説します。

このコードを使うには、HTMLのヘッダのところに以下の関数宣言を挿入します。

<?php

function Find_Latest_From_RSS($filename) {
$str=file_get_contents($filename);
$p = xml_parser_create();
xml_parse_into_struct($p, $str, $vals, $index);
xml_parser_free($p);
for ($i=0; $i<count($vals); $i++) { // skip over stuff before <ITEM>
if ($vals[$i][tag]=="ITEM") {
break;
}
}
$latest="January 1 1970 00:00:00 GMT"; // initialize
for (; $i<count($vals); $i++) { // start looking for PUBDATEs
if ($vals[$i][level]==4 && $vals[$i][tag]=="PUBDATE") {
$dateTime=strtotime($vals[$i][value]);
if ($latest<$dateTime) {
$latest=$dateTime;
}
}
}
return date("n/j/y",$latest);
}

?>

そして更新日付を表示したいところに以下を挿入します。

<?php echo Find_Latest_From_RSS("your_RSS_file"); ?>

例えば、

(<?php echo Find_Latest_From_RSS("your_RSS_file"); ?>更新)

とすれば、
 
(12/11/06更新)

のように表示されます。

日付の表示形式を変えたい場合は、この部分を変更します。

date("n/j/y",$latest)

さて、ここからコードの解説をします。

まず関数の宣言です。

function Find_Latest_From_RSS($filename) {

RSSファイル名を引き渡せるようになっていますので、一つのHTMLファイルの中で複数のRSSファイルを処理する事が可能です。この関数は、戻り値として日付(文字列)を返します。

ここではRSSファイルを一気に読み込みます。
 
$str=file_get_contents($filename);

次の三行はセットになっています。

$p=xml_parser_create();
xml_parse_into_struct($p, $str, $vals, $index);
xml_parser_free($p);

一行目でパーサーオブジェクトを作り、二行目で解析をして、三行目で不要になったパーサーオブジェクトをメモリから消します。解析結果は、$valsの中に入れてくれます。

次に最初の<ITEM>タグを探します。

for ($i=0; $i<count($vals); $i++) { // skip over stuff before <ITEM>
if ($vals[$i][tag]=="ITEM") {
break;
}
}

見つかったらループから抜けます。これは、<ITEM>以前に記述されているRSS feed全体に関する情報を飛び越すためです。

最新のPUBDATEを探すために、$latestという変数を、1970年1月1日に初期化します。

$latest="January 1 1970 00:00:00 GMT";

またforループに入りますが、今度は$iを初期化しません。以前のループを抜けた時の$iの値から繰り返しが始まります。

for (; $i<count($vals); $i++) {

$vals配列内の個々の要素を調べて、「その要素のレベルが4で、タグがPUBDATEだったら」という条件になっています。

if ($vals[$i][level]==4 && $vals[$i][tag]=="PUBDATE") {

レベル4とは、ITEMのPUBDATEが<rss><channel><item>の下に来るから4なのです。レベル3にもPUBDATEがあり、これはRSS feed全体の公開日です。ITEM以前のデータは飛び越していますから、レベルチェックは本当は必要ありませんが、念のためというのと、データ構造を見ていただくために入れました。

上の条件に合った配列要素は、itemの公開日です。これは文字列データですから、比較ができるようにするためにstrtotimeで日付時間データに変換します。

$dateTime=strtotime($vals[$i][value]);

変換後なら、以下のように不等号を使って比較ができます。

if ($latest<$dateTime) {
$latest=$dateTime;
}

より新しい日付が見つかるたびに、$latestを更新していきます。

このように処理していけば、ループが終わった時点で、$latestには、もっとも最近公開されたitemの公開日付が入っているはずです。

最後にこの日付データを好みの形式の文字列に変換してやります。

return date("n/j/y",$latest);

この文字列が、この関数の戻り値となります。

[LINUX] useradd後にsshでログインできない

useraddで新規ユーザを作り、エラーも出なかったので、これでよしと思ったのですが、sshでログインしようとするとPermission deniedでログインできませんでした。ここにLinuxの落とし穴がありました。以下で解説しましょう。

まず

% useradd -gusers -p himitsu -s /bin/bash -d /home/okato -m okato

のように入力します。エラーは出ませんでした。確認したら/home/okatoというユーザディレクトリもできていました。すべて良好と思いsshでログインを試みたら、

$ ssh okato@myserver.com
Permission denied

となってログインできません。

パスワードを変えたりしていてわかったのですが、Linuxは実在する単語などのパスワードを受け付けてくれません。例えばpasswdでパスワード変更する場合にはこのチェックが働きます。ところがuseraddコマンドの時にはこのチェックが裏で働き、ダメなパスワードだとログインできないような設定にされてしまうようです。これは裏で起こっており、エラーメッセージの形でフィードバックされないため、管理者を悩ませる結果となったわけです。

教訓:パスワードは常に強力なものを使おう!

なお、コマンドラインでパスワードを設定する場合は、特殊な記号が使えないことに注意してください。| や>や<などの文字はコマンドラインで特別な意味を持つので、パスワードの一部として使えません。""で囲めばいいかもしれませんが、実験していません。

[LINUX] Can't login after useradd

After creating a new user account with the useradd command, I couldn't login through ssh. Here's a common pitfall to watch out for. Let me discuss it.

I first entered this command as root.

% useradd -gusers -p himitsu -s /bin/bash -d /home/okato -m okato

There was no error. I was able to verify that /home/okato had been created. Thinking that all is well, I proceeded to login through ssh, but I was rejected.

$ ssh okato@myserver.com
Permission denied

After a bit of experimenting, I discovered that Linux checks on your password choice. If it's an existing word or something weak like that, Linux rejects it. You can see this behavior in the passwd command. However, with useradd, apparently, this check operates in the background and if the check doesn't succeed, the user account is rendered unusable. Since this occurs in the background and there's no feedback to the operator, it misleads the operator into thinking all is well.

LESSON TO BE LEARNED: Always use strong passwords!

Also, you should pay attention to use of certain symbols that have special meaning on the command line. Symbols like "|", "<", ">" cannot be used as part of the password to be specified on the command line. You may be able to quote the password and get away with it, but I haven't tried it yet.