WorkPad 用プログラムの組み方(4)

Prev 1 , 2 , 3 , 4, 5 , 6 , 7 , Next


このページの目次


pdb ファイルの構造

おや?まだこのドキュメントを読み続けている??

普通、ここまで欠点だらけの OS を見たら、 そろそろ見捨てるような気がするのだが…まだ?

では、ちょっと WorkPad 上のプログラムを作成することから離れて、 pdb ファイルの構造を説明しよう。 最終的には PalmOS の GUI デザインを理解するためには、 実はここの構造を理解しておく必要がある。


pdb ファイルは PalmOS の database の中でも、 record database を PC に持ってくるためのフォーマットである。 逆に言えば、 pdb ファイルには record database の保持する全ての情報を持っている。 従って、pdb ファイルについて理解すれば database についても理解が深まる。

プログラムの作成には perl5 を使う。 最終的には apart-pdb.perl ファイルのやっていることを理解し、 好きなファイルを pdb ファイルにしたり、 その逆を行うのが目的である。

これができるようになれば、 PC 上の任意のデータを WorkPad に持ってくることも、 その逆もできるようになる。 このデータを WorkPad 側で適切に解釈することができれば、 WorkPad 上でデータを使うことができるようになる。


先に断らなくてはいけない。

このドキュメントを書いた当時、 pdb ファイル並びに prc ファイルのフォーマットに関する 公式のドキュメントは存在しなかった。 従ってこのドキュメントはこの公式のドキュメントを元にしていない。

現在では http://www.palmos.com/dev/tech/docs/fileformats.zip に解説がある。 この公式解説ドキュメントが出たのは 2000年 5月。 PalmOS が出てから一体どれほどの時間が経ったのかを考えると、 Palm Computing 社を自社製品をまじめに考えているとは思えない、 呆れ返るほどの時間が経っている。

これから述べることは複数の人間が解析した結果を あちらこちらから拾ってきてまとめた結果である。

しかし、同時にその解析結果が Palm Computing に支持されているものもある。 Palm OS Emulator(POSE) のプログラムコードとして記述されている pdb 並びに prc ファイルの解析ルーチンがそれである。 具体的には、POSE 30a3 のソースファイル、 LoadApplication.cpp に詳しい。 が、内容に関する詳細な解説はどこにもない。

POSE は Palm Computing によって技術的に援助されているプログラムである。 従って、ここにある PalmOS に関する解析結果の多くは、 Palm Computing によって暗黙に支持されている、と考えて良い。 仮に prc, pdb ファイルのフォーマットが間違っている場合、 POSE は役に立たないことになるが、 これは Palm Computing 社にとっても損失である。

従って、POSE の情報は正しいと仮定して良いだろうし、 その情報をベースにすれば prc, pdb ファイルの フォーマット情報は正しいと思って良いだろう。

もう一つ信頼できると想定して良い情報源は pilot-link のソースコードである。

hacker の解析結果というのは、 時として「プログラマー自身が誤解している公式発表」よりも 正しいことがあり得る。 まぁ、この場合は POSE と同じレベルの hack の賜物なので、 どちらがどちらに比べて正しいと言うことはないが。


pdb ファイルは大きく3つの部分から成り立っている。 database header 部、record header 部、 そして record data 部である。 すでに 前の章 で出てきたように pdb ファイルは data 部が最後に存在する。 通常のファイルの先頭に header を適切につけてやれば、 pdb ファイルになる、ということだ。


database header は 78byte 固定長、 固定フォーマットのフィールドで、pdb ファイルの先頭に存在する。 ここには database 固有の情報が存在する。 78byte の header はさらに次のような情報から成り立っている。

Database Header
offset in byte
from header top
size in byte format meaning
+0x0000 32 Database name Database の PalmOS 上での名前が、 '\0'を含めて格納されている。 pdb ファイルの名前ではなく、 このフィールドに記述されている名前が使われる。
重要な点は、'\0' を含んでいるという点だ。 フィールドの大きさが 32byte ということは、 Database の名前自身としては 31byte しか使えないことを意味する。
また、文字列が 32byte に足りない場合、 残りの部分は '\0' で埋めて欲しいようだ。
実は誰もやっていないと思うのだが、database 名に 「\n\n\n\n\n\n\n\n」のように改行ばっかりを指定したらどうなるんだろう…? もちろん「\t\t\t\t\t」とかでもいいぞ?
+0020 2 flags とりあえず、2byte フィールドである。 big endian(16bit の値を記録するときに、 上位8bit をアドレス +0x20 に、 下位8bit をアドレス +0x21 に保存する、ということ)で記録されている。 $tmpflags として読み出したとしよう。
$tmpflags に対して、
bit 意味
0x8000 このビットが 1 の場合、 この Database は適切に close されていないことを示す。
0x0080 このビットが1の場合、 この Database は file stream を実装するために使われることを示す。 file stream 操作があるのは判っているが、 これが具体的にどういう意味なのかは不明である。 例えば file IO を高速に行うために、 この Database に対する record の扱いが通常の Database とは異るのか、 などは一切判っていない。
0x0040 このビットが1の場合、 この database はコピーしてはいけない/しない。 "should not be copied to" で止められても困るよなぁ…。 to where?! って聞いちゃうよなぁ。 Palm の外か?中か?って。
0x0020 このビットが1の場合、 この database の install 後、device を reset するよう要求する。
0x0010 このビットが1の場合、 Database を open している最中に、 新しい database をインストールしても構わないことを示す。 とあるが、 どういう場合にそんな無茶ができるというのか、 さっぱり判らず。 POSE のようなエミュレーター上でしかつかえないやん。 そんなの。
0x0008 HotSync Manager との HotSync 時には、 PC にバックアップするよう指定する。
0x0004 このビットが1の場合、 Application info block は変更されている。 Application conduit によってオプショナルにサポートされている、そうだ。
0x0002 このビットが1の場合、 このデータベースは read only である。
0x0001 このビットが1の場合、 このデータベースは resource database である。
+0x0022 2 version そのデータベースに対するアプリケーション固有の バージョン番号を格納する。 big endian 2byte の値である。
+0x0024 4 create_time database 作成時刻。 1904年1月1日 0:0:0 からの秒数が、big endian 4byte で格納されている。
+0x0028 4 modified time database を最後に変更した時刻。 1904年1月1日 0:0:0 からの秒数が、big endian 4byte で格納されている。
+0x002c 4 backup time database を最後に HotSync でバックアップした時刻。 1904年1月1日 0:0:0 からの秒数が、big endian 4byte で格納されている。
+0x0030 4 modified number database を変更した回数 が big endian で格納されている。 record の追加、変更、削除が行われる度に番号が増加する、 とある。
+0x0034 4 application info size database に関するアプリケーション固有の情報を格納するのに、 database によっては Application info 領域というのを使う場合がある。 ここには、その Application info block 領域の大きさが big endian で格納されている。
これが 0 以外の値の場合は、 application info 領域が record data 領域の先頭、 オフセット 0 の場所に存在することになる。
なお、resource database であった場合、application info 領域は 存在しない。
+0x0038 4 sort info size database が使う sort information を格納する領域がある場合がある。 ここには、その sort info block 領域の大きさが big endian で 格納されている。
ここが 0 以外の値の場合は、 pdb ファイルの中にはその sort information を格納する領域も存在する。 具体的には、 application info size が 0 の場合は record data 領域の 先頭、オフセット 0 の所に、 application info が存在する場合はその直後に、存在することになる。
なお、resource database であった場合、application info 領域は 存在しない。
+0x003c 4 type database type が入っている。
気をつける必要があるのは、この値は big endian で格納されていることと、 一般には 'abcd' のような非標準的文字列指定で定義されている、 ということである。この場合、big endian で表現すると
'a' * 0x1000000 + 'b' *0x10000 + 'c' * 0x100 + 'd'
の形で格納されている。
+0x0040 4 creatorID database の creatorID を指定する。
気をつける必要があるのは、この値は big endian で格納されていることと、 一般には 'abcd' のような非標準的文字列指定で定義されている、 ということである。この場合、big endian で表現すると
'a' * 0x1000000 + 'b' *0x10000 + 'c' * 0x100 + 'd'
の形で格納されている。
+0x0044 4 unique id seed 各 record には unique id というのが割り振られている。 その値はこの値を元に作られるらしいのだが、 現実問題として、POSE, pilot-link 共にこれは 0 以外の値はなく、 pilot-link では『これは「ごみ」でしかない』とさえ言っている。
一つだけ謎なのは、じゃぁ、「unique id seed」という名前は 一体どこから手にいれたのだろう?そこには何かコメントはなかったのか?
+0x0048 4 next record list 常に 0 。
もし 0 以外の値の場合、prc, pdb ファイルは破損していると考えられるらしい。
だから一体どこからこの名前を手にいれたんだってば?!
+0x004c 2 number of records database に含まれる record の数を指定する。
この値は重要で、次に来る record header 領域の大きさが決まる。

database header 領域の次に来るのが record header 領域である。

record header 領域は、簡単な構造をした element, record header element が database header にあった number of records の数だけ存在する。 さらに、record header 領域の最後には 2 byte の 0x00 で埋められた 領域が存在する。

record header element の構造は record database と resource database では全く異る。 prc, pdb ファイルがどちらなのかは database header の flags フィールド、 0x0001 bit が 1 の場合は resource database, 0 の場合は record database なので、database header を解析することで、 record header element の構造が判明する。 それぞれの場合の構造、サイズは次の通りである。

record database element format
offset in byte
from element top
size in byte format meaning
+0x0000 4 offset record data body の開始位置をファイルの先頭からのバイト数で示している。
record の終了ポイントは明示的に示されることはない。 「次の record の先頭」までがこの record の record body である。 もし、「次」が無い場合は、ファイルの最後までは record body になる。 もし「次の record の先頭」が同じ offset の場合 (あるいは offset が最初から file の終了点を指している場合) この record は「空(size 0)」ということになる。
+0x0004 1 attributes record の attribute が記録されている。 attribute フィールドは 4bit 使われている。 $attribute で 1byte 読み出したとするならば、 各 attribute は次のような意味である。
bit 意味
0x80 Delete on sync.
この bit が1 だということは、 この record は sync 時に消去しなくてはいけないと言うことである。 DmArchiveRecord() や DmDeleteRecord() のように、 record entry を消去しないタイプの削除の際に用いられる。
これは、「HotSync の際に何が削除されたのかが明確に PC に 伝達される必要がある場合」に用いられる。
0x40 Dirty on sync.
この bit が1だということは、 この record は DmReleaseRecord() が実行された際に dirty bit を on にしろ、としていされたと言うことである。
HotSync を行う際には全ての database の全ての record を バックアップするわけではない。 すでに database のバックアップが PC 側にも存在する場合は、 HotSync は変更のあった record だけを PC 側に転送して、 転送にかかる時間を減らそうとする。 「変更があった record」に対するマーキングとして使われるのが、 この『Dirty on sync』 bit である。
もし、Conduit を使ったアプリケーションを書いている場合は、 PC 側に record を転送したら、このbitを0に書き戻してやる必要がある。
0x20 Busy
この bit が 1 であるということは、 何らかのアプリケーションが record を open している (あるいは open したまま終了してしまった) ことを意味している。 本来はこの状態はあり得るべき状態ではない。 Application が実行中に HotSync に応答する、と言うことはあり得ない。 WorkPad 側でも、それまで動いていた Application が終了し、 HotSync Manager が起動されるはずだからだ。
0x10 Secret
この bit が 1 であるということは、 この record はデバイスにユーザーパスワードを 入力しないと表示されないレコードだ、という意味である。
0x0f category
この4bit は「カテゴリ番号」と言われる、 Application がそれぞれの値の意味を決定している 番号が格納される。
+0x0005 3 uniqueID record を識別するための unique ID が記録されている。 値は big endian 3bit で格納されている。
新しく pdb ファイルをつくる場合は 0 を設定しておくと良い。
resource database element format
offset in byte
from element top
size in byte format meaning
+0x0000 4 type resource type を指定している。
各 resource には既存、ユーザー定義の様々な resource 種類が存在できる。 たとえば 'code' はプログラムコードを格納する resource である。
+0x0004 2 id resource ID を指定する。
+0x0006 4 offset 対応する resource データの格納されているポイントを、 file の先頭からのオフセットで示す。
resource データは、「次の resource」の先頭までである。 もし、「次の resource」が存在しない場合は file の終端まで、になる。
また、「次の resource の先頭」や file の終端が、 offset と同じ場合、その resource のサイズは 0 だ、ということである。

record header 部の最後の 2byte(0x00 が2つ)の pad の後には、 record body 部が来る。 record body 部には、実際に使われるデータが格納されている。

record database で、application info size が 0 でない場合は、 record body 部の最初に application info が格納されている。

record database で、sort info size が 0 でない場合は、 sort info が格納されている。 格納場所は application info が存在しない場合は record body 部の 最初に、application info が存在する場合はその後に、 sort info が格納されている。

application info, sort info の後には record data が順に入っている。 record data は record index の順に従っている。


pdb, prc ファイルのフォーマットは拡張性があると言えばいいのか、 ないと言えばいいのか、良く判らない。

例えば、record のサイズは pdb ファイルの先頭からの offset の 組合わせでしていされており、直接指定されていないこともあって、 64kbyte の制限を受けない。 従って、将来 PalmOS から record size の制約が無くなった場合も、 このフォーマットは使い続けることができるだろう。

ところが、record の数は16bit しかないため、 64k個の制限を受けたままである。 これは Palm Device の各種リソースサイズの制約がなくなり、 より多くの record が格納できるようになったときに問題となる。

record サイズの制限が pdb, prc ファイルには存在しない、 という点は、実は CodeWarrior においては問題として発生する。 CodeWarrior はこの 64kbyte の壁をまじめにチェックしていないらしく、 リンカーによって prc ファイルをつくる際に、 64kbyte を越えても全くエラーが発生しない。 これは HotSync を実行して始めてエラーとなるのだが、 その際のエラーでは一体何が起ったのかさっぱり判らない。

もう1つの問題点があり得るとすれば、 それは 4byte に align されていない、という点だろう。

Palm Device がいつまでも MC68000 ベースとは限るまい。 MC68030 等を使うことは MC68020 までの MPU が持っている各種の制約や バグを回避する上で重要だが、 この MPU は 4byte align ベースのアクセスしか許さない、 という性質を持つ。

その一方で、record のサイズ制限がなくなる等した場合、 現在の database を使う必要性などない、という事もできる。 この場合は通常のファイルシステムを RAM disk 上に構築した方が便利だ、 ということだ。 Database というのは確かに極めて限定された世界では有利だが、 汎用性がない。 IBM の System390 と同じ database フォーマットが 最終的に広範的に計算機に適用されなかった理由は、 まさにこの汎用性のなさゆえに単純な「ファイル」という構造に 駆逐されたためである。


Program 1. record header pealer

以上の pdb, prc ファイルのフォーマット情報に基づいて、 perl プログラムを作ってみよう。

最初のプログラムは各種 header 部を引き剥がす、 header pealer をつくってみよう。 オプションを使って application info, sort info を残す、 削るを選択できるようにしよう。

複数ファイルを同時に処理したりはしない。 そんな作業は shell を適切に使えば処理できる。 勝手に既存のファイルを上書きしない(これは stdout に出せばいいからだ)。 パフォーマンスは考えない。 pdb と prc 両方を扱えるように書く。 高度なトリックも使わない。低度なトリックも多分使わない。 判りやすく書くつもりだが、そうなるとは限らない。


コード全体を列挙するのはページの無駄なので さるぅんプロジェクト の PDBHeaderPealer.perl から取ってきて欲しい。 これを使うと、バイナリを理解しないエディターでも、 pdb header は取り除けるだろう。 ただし、PalmOS の特徴の1つである「改行は 0x0a」(MacOS は 0x0d で、 Windows は 0x0d, 0x0a である)問題は解決しない。 それに関しては nkf などを使い対処すること。 そうすれば 先に示したプログラム の出力結果も処理しやすくなるだろう。

プログラムの概略は簡単すぎて説明するのが恥ずかしくなるぐらいだが…。

コメントの後に、いくつか定数項が定義されている。 と言っても Perl には「const」等のキーワードは存在しないので、 単に特定の変数に対して、値を設定しているだけだが。

その後、option 処理を行う。 規定された文字列以外の最初のオプションを入力 pdb ファイル名とし、 処理を行った後正常終了する。 それ以外の場合は、メッセージを出力して終了する。

run_pealer 関数が実際に pdb ファイルから header を引き剥がす 関数の実体である。

まず、必要なファイルを open し、binary mode にする。 STDOUT も binary mode に切り替える。

次に pdb header を読み出す。 その後、record/resource header 分を全て skip する。

Applicatoin info と Sort info 領域を読み出し、 もし、$ApplicationInfo, $SortInfo 変数がそれぞれ 0 以外であれば 読み出した分を STDOUT に書き出す。

後は、残りの部分を読んでは、STDOUT に書き出すのを繰り返す。

まぁ…header を削除するプログラムは、 これ以上手の抜きようもなかろう…というわけでいいものとしましょう。 とりあえず(疑問があったらきっと聞いてくるだろうと言うことで)。


Program 2. record header attacher

Pealer を作ったら、Attacher を作るのが筋というもの。 任意の File に Attacher で Pdb header をつければ、 Database として WorkPad 上で参照できるようになる。 参照できるようになればこっちのもの。 WorkPad 上でいろいろ扱いようもあるというものである。

ここで重要な方針を立てる。曰く、
PDB header をつける ということと File の意味を理解して、適切に record 化する ということとは別の話である。

ファイルシステムがファイルの中身なんぞ知ったことではなく、 全部バイト列として処理するがごとく、 Attacher もファイルを「ファイル」以上の何者とも見なさない。 単なる一定サイズ毎にぶった切った record の羅列に変換する。 Attacher が作った database を適切に読み解くのは (つまり、普通のアプリケーションが File を解析するがごとく、 record を解析していくのは) WorkPad 上の Application の仕事であって、 Attacher のお仕事ではない、とする。


この割り切りを行う pdb file へのコンバーターは、 はっきり言って今のところ PDBHeaderAttacher.perl だけしか見当たらない。

そもそもが、Palm は record を「ただのバイト列」としてしか管理していない。
「OS が record のフォーマットなんぞ知ったことではない」
というのは極めて正しい選択なのだが application はこの record を直接参照することができるために、 application 側に欲目が出てしまっている。 結果、application 毎にフォーマットの異なる、 しかも application にとって非常に解析のしやすい固定ピッチの、 さらに言うならば Machine dependent で Compiler dependent な、 データベースフォーマット、というものが発生してしまっている。

この状態の何がそんなに悪いのか、について 計算機心理学(という言葉が嫌いであれば Computer Usability。 しかし XEROX Parc で開発されたもは計算機科学ではなく、 計算機心理学だ)的に 説明するのは極めて難しい。 非常に多くの場所に「人間が頑張れば」とか 「フォーマットコンバーターを導入すれば」と言った 例外処理を導入することができるからだ。 一方でメリットは明白で、 メモリスペースの省力化とアプリケーションの単純化が計れることは 幼稚園児にでも判るだろう。 このため、Palm Co. が出している様々なドキュメントでも、 この傾向は増長される傾向にある。

しかし、これは絶対に取ってはいけない方針である。 データとアプリケーションは明白に分離されなくてはいけないし、 アプリケーションが複雑になってでも データは可能な限り非固定長フォーマットで記述される必要があるのだ。

計算機心理額的に説明するのは複雑で面倒なので、1つの例を挙げよう。 specific なフォーマットを採用すると、 この例で起ったことと同質のことが あなたのアプリケーションにおいても生じるからだ。

例とは「2000年問題」である。

そもそも、2000年問題というのは、 『計算機が2000年を正しく判断できない問題』 の事ではない。 銀行などのデータベースに 2000年以降の処理を記述できない というのが 2000年問題なのだ。 それ以降に出てきた、「2000年になると時計が狂う」という問題は、 2000年問題なのではなく、 「チップをけちりすぎた結果生じたバグ」でしかない。 まずここを混同しないよう、指摘しておこう。

その上で。 では、なぜ「データベースに2000年以降の処理を記述できな」かったか? 答は簡単で、年のフィールドが10進数2桁分しかなかったからだ。 2桁しかないのでは、1900年代の分しか処理できない。

なぜそんなことをしたのか? このデータベースのフォーマットが決まった当時、 メモリもディスクスペースも、オープンリールテープも、 とても高かったからだ。 1970年代前半までのプログラマーにとって、 「技」と言ったら「メモリをどうやってセーブするか」ということだったのだ。 そうすれば計算機のコストが安くなるので、 利益率が増大する。 そう、当時は「プログラム」は「計算機」に付随するものでしかなく、 計算機を買うときには「プログラム付きで」買っていたのだ。

彼らはデータ構造を極めて「汎用に」しようとはしていた。 でなければ 10進数2桁ではなく、16進数2桁という値を使っていただろう。 この方がデータをより小さくできるのだから。

別に当時のプログラマーが2000年が来ることを知らなかったわけではない。 彼らはこう思っていたのだ:
「2000年までは 25年もあるじゃないか。 それまでにはこのプログラムはきっと時代遅れになっているよ」

彼らは半分だけ正しかった。 確かにプログラムは時代遅れになった。 確かに計算機そのものは時代遅れになり、破棄された。

彼らが間違っていた半分とは、
「確かにプログラムは破棄されたし、計算機も破棄されたが、データは残った」
という点である。 データが残ったのはそのデータを作った人たちが それを使い続けたかったからだ。 銀行などの計算機消費者は、 計算機を取り替える度に前の計算機に入っていたデータを全て破棄する、 などということには我慢ならなかったのだ。 なにしろ、そのデータこそが彼らの生命線だったわけだし、 そのデータを再入力するなどとんでもない話だからだ。 そんな人件費をどうしてかける必要がある? プログラムを適応させた方がいいじゃないか? 新しい計算機は早いのだから。

こうしてデータと、そのデータのフォーマットは生き残った。 年を表すフィールドは相変わらず10進数2桁で表現されていた。

不幸は、データを保存するためのリソース、 つまり HDD の容量と値段の低下速度があまりにも速かったことにある。 あまりにも速く、あまりにも廉価になったため、 電子取引は急速に広がっていった。 データフォーマットに問題を残したままで。 また、一方で電子取引は人間が常時作業するのと異なるため、 24時間の取引を可能にしてしまった。 システムが停止するのは正月の3ヶ日ぐらいなものになってしまった。

そして、ある日、誰かが気がついたのだ:
「おや?データフォーマットに問題があるぞ?」
そして別の人が気がついたのだ:
「おい、3ヶ日ではデータを書き換えきれないぐらい、データがあるぞ」
そして 2000年問題が騒ぎ立てられはじめたわけだ。

2000年問題は問題がもっとも広汎に発現した例だが、 もっとも端的に何が起るのかを示している。

これらを総合するとこうなる。
「WorkPad 用のプログラムだからと言って、 データフォーマットを WorkPad でさえ使えれば良い、 などとしていると、後でしっぺ返しを食らう」

この観点で行くと、database という形式を選んだ PalmOS 自体、 十分な先見の明があったとは言えない。 pdb フォーマットも不適切と言える。

ただ、pdb ファイルは header を引き剥がしさえすれば普通の ファイルにすることができる。 当然その逆をやり、application でデータの解析を行うようにすれば、 問題は最小限に抑えられるだろう。




コード全体を列挙するのはページの無駄なので さるぅんプロジェクト の PDBHeaderAttacher.perl から取ってきて欲しい。

プログラムのほとんどの部分は option 処理である。 見ての通り何のチェックもしていないので、 下手な使い方をすると破綻するだろう。

実質的な部分は run_attacher という関数にある。 と言っても、

という事以外はあまりにも「そのまんま」なコードなので解説は ほとんど要らないだろう。 説明が欲しい人はメールを欲しい。 人数が多ければ考えるが…普通要らないよね? (コードを組むのにかかった時間も1時間だし、 そのうち40分は usage() にかかっているていたらくだ)


このプログラムは、単にサンプル以上の意味が、実はある。 このプログラムができたおかげで、 unix でいう.ほげほげrc ファイル相当の database が作れる。

.ほげほげrc というのは、 unix を使っている人にとっては簡単に理解できるだろうが、 『ほげほげ』というプログラムにとっての「設定ファイル」である。

このファイルがいつからこのファイル名になったのかは判らない。 おそらく、BSD unix の .cshrc が始めてではないか? と思われるが確証はない。誰か知ってたら教えて欲しい。

unix のプログラムは大抵の場合、 起動時にまずデフォルトの設定というのを持っているが、 それを「設定ファイル」というもので置き換え、 各ユーザーの好みにあった動きをするようにチューンできる。 unix はマルチユーザー OS なので「特定の人の好みに合わせたバイナリ」 というものを用意するのは現実的ではなかったのだ。

また、このファイルはテキストファイルであった。 このため、変更のために専用のツールを必要とせず、 移植が容易であり、 最終的に unix の強力な移植性と unix 間での環境移動のしやすさをもたらした。

テキストファイル処理に伴うオーバーヘッドが問題とされなかったのは、 決して unix マシンが resource が十分あるマシンだからではない。 実際、初期の unix マシンには 64kbyte のコードサイズと、 64kbyte の 作業用 RAM 領域しかないもの(これでも「贅沢」なのだが) がほとんどだった。 このサイズは PalmOS のメモリ量以下なのはすぐ判るだろう。 つまり、当時のマシンに可能である以上、 PalmOS にとってもこの構造は決して「贅沢」な使い方ではないはずだ。

では、なぜテキストファイルだったのか。 それは「使いやすさ」のためだ。

バイナリファイルによる設定ファイルに専用の設定ツールを作ると、 そのアプリケーションもまた設定ファイルを必要とし、 その設定ファイルのためにまた設定ツールが必要になる… これは無意味な無限ループを作り上げる。

各アプリケーション自体に設定変更機能を追加することは可能だが、 それはアプリケーションを増大させ、 結果として「ある一定の code size restriction」の中で 実装可能な機能が制限されてしまう。

結果、「汎用的なバイナリファイルエディター」と 「汎用的なテキストエディター」のどちらかを使って 設定ファイルを作るようにするしかなかったのだ。 そして、「汎用的なテキストエディター」はこの必要性以外でも どのみち必要とする。 であれば、テキストエディターだけで十分だ、と言うことになる。

この判断ルールは全く同様のことが PalmOS についても言える。 汎用のテキストファイル(というかテキストファイルを database 化したもの) で設定を記述することができれば、 アプリケーションごとに設定プログラムを作る必要も、 アプリケーション内に設定変更機能を追加する必要もない。

テキストによる設定ファイルが作れる、と言うこの条件はさらに、 locale ファイルを作成することで localize 処理をも簡単にする。 J-OS の Localizer は既存のアプリケーションを各国語対応できるという 意味では有能だが、 アプリケーションを最初から多言語対応に作ろうとする場合には これだけでは不十分である(PalmOS の機能の方がこれでは不十分なので、 Localizer にも限界はある、という意味だ)。 結局多言語対応はアプリケーション設計段階での対応を必要とし、 その作り込みには locale ファイルを必要とする。

「設定ファイル」も「locale ファイル」もユーザーが自由に 変更しやすいテキストフォーマットであれば、 より自由で使い勝手の良いアプリケーションを作り上げることができるだろう。


Program 3. database splitter.

1つの database を 1つの file にする方法と、 1つの file を 1つの database にする方法が手に入ったら、 次は 1つの database を複数の file にする方法と、 複数の file を 1つの database にまとめあげる方法が必要になる。 つまり、record ごとの分解と再構成のためのツールだ。

1つの database を record 単位に分解する perl script として、 split-pdb.perl というものを作ろう。 正確には「作り直そう」。 これは旧来 apart-pdb.perl という名前だった script を直す、という意味だ。 2000/02/14 現在の apart-pdb.perl は prc ファイルに対して 正常に処理を行うことができていない。 これを直してしまえば、prc/pdb ファイルの別なく、 全ての database ファイルを分解することができる。

この split-pdb.perl はもうひとつだけ欲目を出そう。 join-pdb.perl というファイルをこのさらに後に作るわけだが (そしてそれはバラバラのファイルをそれぞれ record として 登録するものになるわけだが)、 フォーマットを互換性のあるものにしよう。 つまり、split-pdb.perl で分解したイメージはそのまま join-pdb.perl で同じイメージに再合成可能なようにするわけだ。

その代わり、1つの犠牲を払おう。 split-pdb.perl はこれからデザインするわけだが、 既存の split-pdb.perl の出力と互換性を持たせることは考えない。 いや、最終的に互換性があっても構わないのだがその事にはこだわらない。


split-pdb.perl の出力は、1 record data image に対して 1 file とする。 この record data には application info, sort info も含める。 従って、database header, record header をどこか別の所に出力し、 そこには record data image file 名の情報が追加されるべきである。

この段階で、record image は stdout のような標準出力へは 出力できないことになる。 そもそも1つのものを複数に分配するのだから、stdout のような1つしかない 「口」では役不足なのは明らかだ。 そこで、「database header, record header」を stdout に出力し、 record image は「split-pdb.perl 指定の」file naming rule で 出力することにしよう。 これで、問題になるのは stdout で出力するファイルのフォーマットだけになる。 以後、これを「出力ファイル」と呼ぶことにしよう。

出力ファイルはテキストファイルとする。 テキストファイルは『perl がテキストと認識できるもの』で書かれている ものとする。つまり今後出てくる「空文字」や「改行」、「行末」は それぞれの perl に依存するものとする。 特に「改行」文字は OS ごとに異るので、 perl にお任せと言うことにする。

出力ファイルにはコメントが書けることにする。 コメントは # ではじまり、その行末までをコメントとする。 コメントは処理の最初に削除され、 「単なる改行コード」と同じ扱いになることとする。

perl において、正規表現で /^\s*$/ で表される行 (この中にはコメントを削除した後に上記正規表現にマッチするものも含む) は、 「空行」と等価であると仮定する。 これはつまり全角の空白「 」だけの行は「空行ではない」ということでもある。

pdb ファイル名やファイルサイズなど、 直接的には関係のない情報は全てコメントで記述することにする。 これには、record offset のような、 「分離後は価値を持たない」 情報も、 「record size」のように直接はしていされていない情報も、 含めることとする。

database header は

Database {
    .....
}

で囲まれるものとする。 重要なポイントは、 Database { はこれ単体で1行、 閉じる方の }も単体で1行でなくてはならない、という点だ。

record header は

Record {
    .....
}

で囲まれるものとする。 Record {Database {同様これ単体で1行、 閉じる方も同様である。 Application Info, Sort info はそれぞれ Application { Sort { も同様である。

Database や Record の各要素は基本的に

    version    12345

のように \s*<name>\s+<value>\n の組で成り立つとする。

value が数値の場合、表現は4通りあり得るものとする。 0 以外の数値ではじまる場合、それは 10進数であるとする。 0b ではじまる場合、それは2進数表現であるとする。 0x ではじまる場合、それは16進数表現であるとする。 0 ではじまるそれ以外の場合は 8進数表現であるとする。

value が文字列の場合、'.... ' のように ' で囲まれているとする。 さらに、文字列の中に ' を含める場合は\'と、 \ を含める場合は\\と表現するものとする。 \xab のように \x ではじまると、
次の2文字と合わせて4文字で 1byte を16進数で表現されているものとする。 さらに、0x09 を \t, 0x0a を \n, 0x0d を \r で表すものとする。 つまり、database 名は完全に1行に収まる。
基本的に non ASCII 文字コードは、すでに出ているコードを除いて 16進表現で表すこととする。 ただし、\x00 だけは「database 名の最後の文字コード」として、 表示しない。
また漢字での表現はしない。 これは、sjis,jis,euc,unicode,ebcdic などを処理するのが面倒すぎるからだ。 勘弁してくれ。

value が bit field のように 1, 0 の場合、 各ビットごとに 1 の場合、0 の場合の記号が定義されているとする。 その意味はコメントで書かれているとする。 1,0 では定義しない。 どっちがどっちやら…という状態が発生すると厄介だからだ。

Database は空行を除いて、最初に定義されていなくてはいけない。 Record は Database の後に「record 0 から順番に」定義されてなくてはいけない。

name は順不同で並ぶ。 1つの group の中で同じ名前の値が複数回定義されてはいけないし、 複数回定義しない。 また、定義が不足している、という状態が起こってもいけないし、 起さない。つまり「デフォルト」という状態は存在しない。

Application, sort, record data のそれぞれが、 サイズ 0 の場合、data を書き出すファイルを作らない。 この場合はファイル名は「空」になる。 '' ではなく 「なんもなし」 になる。

こんな感じかしら?


プログラムコードは さるぅんプロジェクト の split.perl から獲得して欲しい。

split.perl <pdb file> <file output directory>

が正しい起動方法とする。 これ以外の方法で起動した場合、あるいは pdb file がない、 file output directory が見つからない、 などの場合はヘルプメッセージを出力して終了する。

option は存在しない。 --help すら用意しない。 そうすればファイル名指定とかが楽だからね。

pdb file 名に - を指定すると(perl のルールに従って) stdin になる。 - というファイル名は使えないとする。

同様に file output directory 名に - は指定できない。 さらに、file output directory はすでに存在してなくてはいけない。 split.perl が利用するファイルと同じ名前のファイルがすでに存在する場合、 そのファイルは単純に上書きされる。 既存ファイルが保存されることはない。 もし既存ファイルが書き込み禁止であった場合、 そこで動作はそこで停止する。


最後につくらなくてはいけないのは、 split.perl の逆をするプログラム、 つまりバラバラになっているバイナリファイルを1つの prc/pdb ファイルに 組み立てるプログラムだ。




Prev 1 , 2 , 3 , 4, 5 , 6 , 7 , Next


このページに関する コメント、情報、誤りなどの情報がありましたら、 奥山 まで。

2000-07-14 やっとこさ、split.perl ができた。
でも join.perl がまだ全然… こいつが完成せんとこのページは終わらないというのに。

2000-08-27 下佐粉さんに、 http://www.palmos.com/dev/tech/docs/fileformats.zip の存在を教えてもらい、アドレスを追加する。