玩式草子─ソフトウェアとたわむれる日々

第2回 「あなたを,犯人です」 ─ デバッグという名のミステリー

この記事を読むのに必要な時間:およそ 9.5 分

メッセージ出力箇所の調査

今回出力されているメッセージは図1のように「"書庫ファイル.tar.bz2"が開けませんでした」⁠ファイルが見つかりませんでした」の2種類です。これらのメッセージがどこで出力されているかを調べれば,処理の流れがわかるかも知れません。

ソフトウェアが出力するメッセージは全てソースコードの中に書かれています。そのため,伝統的なソフトウェアでは英文のエラーメッセージを元にソースコードをgrepするだけで出力箇所を特定することが可能でしたが,file-roller も含めて国際化機能に対応した最近のソフトウェアの場合は,出力されるメッセージ(今回の例では日本語のエラーメッセージ)がそのままCのソースコードに埋め込まれているわけではないので注意が必要です。

国際化機能に対応したソフトウェアでは,Cライブラリの持つlocale(ロケール)という機能を使って,出力時にソースコード中のメッセージを各言語に合わせた文章に自動的に変換しています。変換処理は言語ごとに文字列の対応表を用意することで実現しており,日本語の場合はソースコードディレクトリのpo/ja.poというファイルに,ソースコード中のメッセージとそれに対応する日本語の文字列が集められています。

ソースコードをコンパイルすると,ja.po ファイルはバイナリ化されたja.gmoファイルに変換され,このja.gmoファイルが/usr/share/locale/ja/LC_MESSAGE/file-roller.mo というファイル(メッセージカタログ)にインストールされます。C ライブラリはこのファイルを用いてソフトウェアに埋め込まれた英文の出力メッセージを動的に日本語に変換して出力しています。

それではこのja.poファイルを見てましょう。なお,file-rollerのja.poファイルはUTF-8のエンコーディングで記述されているので,Plamo Linuxの環境で見るにはUTF-8に対応したlvやemacsで開く必要があります。

リスト1 file-rollerのja.po(先頭部分)

# file-roller ja.po.
# Copyright (C) 2001-2009 Free Software Foundation, Inc.
# Takeshi AIHANA <takeshi.aihana@gmail.com>, 2001-2009.
#
msgid ""
msgstr ""
"Project-Id-Version: file-roller trunk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-02-22 10:38+0900\n"
"PO-Revision-Date: 2009-02-22 10:28+0900\n"
"Last-Translator: Takeshi AIHANA <takeshi.aihana@gmail.com>\n"
"Language-Team: Japanese <gnome-translation@gnome.gr.jp>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#: ../copy-n-paste/eggdesktopfile.c:165
#, c-format
msgid "File is not a valid .desktop file"
msgstr "妥当な .desktop ファイルではありません"

#: ../copy-n-paste/eggdesktopfile.c:188
#, c-format
msgid "Unrecognized desktop file Version '%s'"
msgstr "バージョン '%s' の .desktop ファイルはサポートしていません"

このように,ja.poファイルはソースコードのどのファイルのどの行で出力される英文のメッセージがどういう日本語に対応するかを逐一記述したファイルになっています。そのため,出力された日本語のメッセージ(⁠⁠開けませんでした」⁠ファイルが見つかりませんでした」等)を元に調べれば,出力箇所が特定できるはずです。今回の例ではどうやらこのあたりのようです。

#: ../src/fr-archive.c:1225
#, c-format
msgid "The file doesn't exist"
msgstr "ファイルが見つかりませんでした。"
...
#: ../src/fr-window.c:2908
#, c-format
msgid "Could not open \"%s\""
msgstr "\"%s\" を開けませんでした"

この結果から,それぞれの該当箇所を調べてみます。

src/fr-archive.c の1225行目はこのあたりです。

リスト2 fr-archive.c(1225行目付近)

1217 static void
1218 copy_remote_file (FrArchive  *archive,
1219                   const char *password) 
1220 { 
1221         XferData *xfer_data; 
1222  
1223         if (! g_file_query_exists (archive->file, NULL)) {
1224                 GError *error;
1225                 error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("The file doesn't exist"));
1226                 fr_archive_copy_done (archive, FR_ACTION_LOADING_ARCHIVE, error);
1227                 g_error_free (error);
1228                 return;
1229         }

src/fr-window.cでは多少行番号がずれているようですが,"Could not open" というメッセージは1ヵ所だけなのでこの部分でしょう。

リスト3 fr-window.c(2920行目付近)

2917                 case FR_ACTION_LOADING_ARCHIVE:
2918                         dialog_parent = window->priv->load_error_parent_window;
2919                         utf8_name = g_uri_display_basename (window->priv->archive_uri);
2920                         msg = g_strdup_printf (_("Could not open \"%s\""), utf8_name);
2921                         g_free (utf8_name);
2922                         break;

このコードを眺めると,前者では archive->file の有無を g_file_query_exists() という関数で調べて,見つからなかったからエラーを出す処理,後者ではこの case 文自体が handle_errors() という関数の中にあり,すでに生じたエラー(FR_ACTION_LOADING_ARCHIVE)に応じて,エラーメッセージと開けなかったファイルの名前(utf8_name)を表示するようになっているようです。

po/ja.poファイルは,ある程度ソースコードとは独立してメンテナンスされているので,ja.poファイルの生成後にソースコードを更新すると,上記のように行番号がずれることがあります。行番号が多少ずれても,文字列の変換は行番号とは無関係に行われるので,表示されるメッセージそのものが変更されない限り,各国語への変換処理は正しく行われます。

これらのファイルの名前がどうなっているかを調べるために,それぞれのファイルの該当箇所にprintf()文を追加してみました。

リスト4 fr-archive.cにprintfを追加

1221         XferDataxfer_data;
1222  
1223         printf("my_debug/fr-archive.c:  archive->file:%s\n", archive->file);
1224         if (! g_file_query_exists (archive->file, NULL)) {

リスト5 fr-window.cにprintfを追加

2919                         utf8_name = g_uri_display_basename (window->priv->archive_uri);
2920                         printf("my_debug/fr-window.c: utf8_name:%s\n", utf8_name);
2921                         msg = g_strdup_printf (_("Could not open \"%s\""), utf8_name);

デバッグメッセージを追加したソースコードをコンパイルし直して,日本語の書庫ファイルを引数に実行してみると,コンソールには図3のような出力が出ました。

図3 デバッグメッセージの出力

図3 デバッグメッセージの出力

コマンドは2度実行しており,1度目の結果を見ると出力される文字のエンコーディングがUTF-8のようだったので,2度目では出力結果をnkfに通してEUC-JPに変換しています。

この結果を見ると,fr-window.cではファイル名が正しくUTF-8になっているものの,fr-archive.cのcopy_remote_file()関数の中のarchive->fileはファイル名が壊れてしまっているようです。

そこで,この copy_remote_file() がどこから呼ばれているかを調べてることにしました。

著者プロフィール

こじまみつひろ

Plamo Linuxとりまとめ役。もともとは人類学的にハッカー文化を研究しようとしていたものの,いつの間にかミイラ取りがミイラになってOSSの世界にどっぷりと漬かってしまいました。最近は田舎に隠棲して半農半自営な生活をしながらソフトウェアと戯れています。

URLhttp://www.linet.gr.jp/~kojima/Plamo/index.html