Previous: msgunfmt Invocation, Up: Binaries [Contents][Index]
生成されたMOファイルの書式については、以下のような図を用いて説明するのがよいでしょう。
最初の2wordには、ファイルの識別が含まれます。magic numberは常にGNU
MOファイルを意味するnumberになります。numberは生成されたマシンのバイトオーダーにしたがって格納されます。つまり実際のmagic
numberは、0x950412de
と0xde120495
のいずれかです。
2番目のwordは、ファイル書式の現在のrevisionを説明し、major revision number、およびminor revision numberから成り立ちます。revision numberにより、MOファイルの読み手は、古い書式と新しい書式を識別して、(可能なかぎり)内容を処理できます。いまのところmajor revisionは0か1で、minor revisionも0と1ですが、将来は追加されるかもしれません。想定外のmajor revision numberの場合、プログラムはMOファイル全体を読み込むのを中止する必要があります。想定外のminor revision numberは、ファイルは読み込めても、すべての内容は読み込めないことを意味します。プログラムが解析できるのは、より小さなminor revision numberのときだけです。
異なるmagic numberによって書式の違いを表すのではなく、magic numberとは別にversionが保持されます。これは/etc/magicが滅多に更新されないことが主な理由です。
MOファイルの冒頭部の情報が拡張されたときに、それらを読み込むプログラムをリコンパイルしなくても良いように、以降のテーブルのポインターを使ってください。そのようにしておけば、後で新しいフラグのビットを追加したり、使用されている文字コードの表示や、新しいテーブルなどを挿入されたときに便利です。
図中のoffset Oとoffset Tには、2つのstring descriptorを見出すことができます。この2つのテーブルでは、どちらもstring descriptorとして 32ビットの整数が使用されており、1つは文字列の長さを示し、もう1つは文字列がMOファイルの先頭から何バイト目かというoffsetを示します。最初のテーブルには元の文字列のdescriptorが含まれていて、これらの元文字列は辞書順にソートされて格納されています。2番目のテーブルには翻訳された文字列のdescriptorが含まれており、これらは1番目のテーブルに対応しています。つまり1番目のテーブルと同じ添字で2番目のテーブルにアクセスすれば、対応する翻訳を取得できます。
元の文字列をソートして格納することにより、MOファイルにハッシュテーブルを含まれていなかったり、含まれていたとしても実際に使うことができないときにも、単純な二分探索が可能になります。これには他にも利点があります。GNU
gettext
は、POファイルの空の文字列にたいする翻訳文字列として、MOファイルに付加するシステム情報を割り当てます。この空文字列と翻訳のペアが、元の文字列のテーブルと、翻訳文字列のテーブルの最初に配置されることにより、システム情報を簡単に見つけることができるのです。
ハッシュテーブルのサイズSが0のときもあります。これは、ハッシュテーブル自体がMOファイルに含まれていない場合です。事前に算出されたハッシュテーブルはディスク容量を消費し、速度も早くないという理由で、この方式を好む人もいます。ハッシュテーブルは、MOファイル中の文字列のソートされた配列の添字を含んでいます。競合はdouble
hashingにより解決しています。使用されている正確なhashing algorithmは、GNU
gettext
のコード実装の説明になってしまうので、ここでは説明しません。
ハッシュテーブルを参照して取得する文字列自体はNUL終端されており、string
descriptorの文字列長にそのNULの分は含まれません。msgfmt
プログラムには、MOファイル中の文字列のインデントを選択するオプションがあります。このオプションを指定すると、個々の文字列の開始位置のオフセットは指定されたインデント値の倍数分ずれます。RISCマシンには、適したインデント指定によって速度が改善するものがあります。
contextについては、元の文字列の代わりに、context文字列と元の文字列をEOTで連結したものが、ソートされて格納されます。
plural formについては、元の文字列のsingularとpluralがNULで区切られて格納されます。string descriptorには、両方の長さが記述されます。しかし、ハッシュテーブルを参照するときは、元の文字列のsingularだけが使用されます。さまざまなpluralにたいする翻訳は、すべてNUL区切りで格納されます。この場合もstring descriptorには、それらすべての長さが格納されます。
MOファイル内の文字列にNULが埋め込まれることを防ぐ方法はありません。しかし現在のプログラムのインターフェースは、文字列がNULで終端されると仮定しているため、文字列の途中にNULが埋め込まれている場合、何らかの不都合が起こり得ます。MOファイルの書式は、後から他のインターフェースを適用できるほどには一般的です。一例としては、意図しないNULが出現するような箇所にwide characterを使用する方法などがあります(実際にはMOファイル中にwide characterを保持することはしません。wide characterを使用するとファイルの容量が不必要に大きくなります。また‘wchar_t’はプラットフォームに依存するため、MOファイルもプラットフォームに依存することになるからです)。
この技術的な問題は、GNU gettext
のdevelopment
forumで盛んに議論されており、MOファイルの書式が将来、進化・変更されることが予想されます。その可能性としては、同時に複数の書式にたいするサポートさえもが含まれます。しかし、わたしたちに何らかの出発点が必要なことは確かで、ここで説明しているMOファイルの書式はよい出発点でした。今の書式には厳密な制約もなく、後から書式を拡張するのは簡単なので、わたしたちは現在のアプローチに満足しています。
byte +------------------------------------------+ 0 | magic number = 0x950412de | | | 4 | file format revision = 0 | | | 8 | number of strings | == N | | 12 | offset of table with original strings | == O | | 16 | offset of table with translation strings | == T | | 20 | size of hashing table | == S | | 24 | offset of hashing table | == H | | . . . (possibly more entries later) . . . | | O | length & offset 0th string ----------------. O + 8 | length & offset 1st string ------------------. ... ... | | O + ((N-1)*8)| length & offset (N-1)th string | | | | | | | T | length & offset 0th translation ---------------. T + 8 | length & offset 1st translation -----------------. ... ... | | | | T + ((N-1)*8)| length & offset (N-1)th translation | | | | | | | | | | | H | start hash table | | | | | ... ... | | | | H + S * 4 | end hash table | | | | | | | | | | | | NUL terminated 0th string <----------------' | | | | | | | | | NUL terminated 1st string <------------------' | | | | | | ... ... | | | | | | | NUL terminated 0th translation <---------------' | | | | | NUL terminated 1st translation <-----------------' | | ... ... | | +------------------------------------------+