策 } 着 ] プログラミ / グ 6 図 3 新か活しれた ah-tty > の 図 4 satoruØpearI: - / 阯 ah ・ Y ・ 0.3.12 at げ第第は : ~ / 町℃ / 面ー tt リ -0.3.1 オ図 い .3 」 : 以 トライ ◎◎ a m Z いに L. ハせに tt 幻 , せ第名 0 印しりび n 」 , n a 1 し、 pt 0 【 as . chatty は、端末工ミュレーションなどの複雑な処理を おこなっていないため、このような問題は起きない。 chatty の仕組み chatty は、ユーザーの入力を監視し、辞書に登録され た文字列か盟 t ると端末のタイトノレヾーにメッセージを出 力する。このイはみは、 4 月号で紹介した ttyrec と同様、 script コマンド 5 を改造して実現した。 script のソースを読むと、入力を処理する doinput と char ibuf [BUFS 工 Z] ; register cc; doinput ( ) void いう関数がある。 (void) write(master, ibuf , (c) ; while ((cc = read(), ibuf , BUFSIZ) ) #endif (void) close(slave) ; #ifdef HAVE_openpty (void) fclose(fscript) ; (void) write(master, ibuf , (c) ; watch—input (dict , revbuf , ibuf , while ((cc = read(), ibuf , BUFSIZ) ) 監視する watch-input 関数を」助日した。 そこで、この関数の while ループにユーザーの入力を done ( ) ; 言川は、本誌 2002 年 3 月号の「プログラミング・テクニック」を参 5 端末に出力されたすべての文字列をファイルに保存するコマンドである。 ドはすべて取り除いている。 ルに保存する処理があるが、 chatty ではこの部分のコー script コマンドには、端末に出力された文字列をファイ 122 X ◎ 辞書のデータ構造 能な、、木 (tree)" を使うことにした。 前方一致オはおこなえない。そこで、前方一致検索か可 えば m というキーから make や mail 、 man をみつける はキーに完全に -- - ・致する項目しか検索できないため、たと 辞書検索に適したデータ構造である。しかし、ノ、ツシュで 次の↑甫としてハッシュを考えた。ハッシュは高速な 0 はおもしろくないという理由もあって、採用しないことに にならないが、リストで実装してもプログラミングとして 点がある。上記のような小さな辞書なら検索の速度は間題 登録される項目か増えるにつれて検索か遅くなるという欠 て、最初に考えたのはリストである。しかし、リストには、 このような辞書をプログラムで表現するデータ構造とし xman うげ。こんなのあったのか man google 使えよ mail 仕事しないでメールばっかり make ソースからビルドかよ 加するには、このファイルを編集すればいい。 ル chatty-dict. ja として保存しておく。メッセージを追 メッセージは次のような形式で定義し、テキストファイ UNIX MAGAZINE 2002.7 始する。各節はキーの文字の不頁と同数の枝をもっている トライの探索は、矢印で示した最 - ヒ部の節 ( から開 る。メッセージをもつ節 (node) は二重円で示している。 る。上記の辞書をトライで表現すると、図 4 のようにな 高速な辞書検索を寒見する木に、、トライ (TRIE)" があ
し inux のプートプロセス 3 いないので、実在しないプロセス ID は指定しないでくだ さい。また、スワップデーモンなどのカーネルスレッド は、プロセスではないため言ムみ工ラーとなります。 リスト 1 おまけのプログラム (read-ioperm ・ c) #define __KERNEL #define SEEK_SET 0 #include く linux/mm. h> #include く linux/sched. h> #include く asm—i386/page . 五 > #define pidhash—addr Oxc027dbe0 void readkmem (int fd, void *buf , unsigned off , exit(l); perror ( "kmem read" ) ・ if (read(fd, buf, (z) ! = (z) { exit(l) ; perror("kmem lseek") ; (lseek(fd, off , SEEK_SET) ! = off) { if なお、このプログラムはカーネルの領域を直接読み込む int sz) ので、実行には root の権限が必喫です。 void find—task(int fd, int pid, struct task_struct *task) struct task_struct *htable_ptr ; readkmem()d , &htable_ptr , pidhash_addr + (pid_hashfn(pid) * 4 ) , sizeof (htable—ptr) ) ; while ( 1 ) { readkmem(fd, task, (unsigned) htable_ptr, sizeof (*task)) ; if (pid task—>pid) { return ; htable—ptr = task—>next—task ; int main(int argc , char *argv ロ ) int i , kmemfd, memfd; struct task_struct task; struct thread_struct thread; UNIX MAGAZINE 2002.7 pid = atoi(argv[l]) ; exit(l); printf("%s pid\n" , argv[O] ) ; if (argc ! = 2 ) { unsigned long eflags ; int pid, iopl ; 61
ー横 } 着】ブログラミ / グ 6 dictnode-traverse 関数では、木の全探索をおこない、 メッセージを含む節をみつけるたびに引数て指定されたコ ーノレヾック関数を適用する ( 図 9 ) 。 たとえば、木に登録されているキーとメッセージをすべ て表示するには、 static void print—entry (const char *key , const Char *value , VOid printf("%s\t%s\n" , key, value) ; という表示用のコーノレヾック関数を定義し、 dictnode—traverse(root , print—entry, *data) NULL ) ; のように、 dictnode-traverse 関数に、木の根 (root) と ともにコーノレヾック関数の関数ポインタ (print-entry) を 渡せはいい。この例では、 dictnode-traverse 関数の第 3 引数は利用していないが、コーノレヾック関数にデータを渡 したいときはここに指定する。たとえば、 のように、カウントしながら表示するには、 うげ。こんなのあったのか google 使えよ 仕事しないでメールばっかり ソースからビルドかよ 4 namx 3 nam 2 liam 1 ekam static void print—entry (const char *key , const Char *value , int *count = (int *)data; (*count) 十十 ; printf("%d %s\t%s\n" *count , key , value) ; void *data) というバ用のコーノレヾック関数を定義し、次のように dictnode_traverse(root , print_entry , &count) ; int count = 0 ; て dictnode-traverse 関数を呼び出せはいい。 126 厄介なコードを、、涙ぐましいコード " と呼んでいる。 必要がある。私は、言語仕様の制限に起因するこのような でできるが、 C 言語では、このように面倒なコードを書く ラミング言語では、、クロージャ " という機構を用いて一瞬 ビジターパターンは、 Scheme や Ruby といったプログ たデータ data をもとの型のポインタに戻す必要がある。 コーノレヾック関数のなかでは、 void ポインタで渡され 辞書の内部実装の交換 前述のとおり、 chatty では辞書のデータ構造としてト ライを簡ヒした木を使っている。しかし、将来的に必要 に迫られたり、気力畯わって別のデータ構造に変更したく なるかもしれない。 そこで、辞書検索の部分のコード (dict. [ch]) では、内 部のデータ構造を簡単に交換できるように、内部の実装と は独立して外向きの API を定義している。具体的には、 タ粫にの API として、 Dict という型に対して次のような 関数を用意した。 ・ dict-new : 辞書を新規作成 ・ dict-destroy : 辞書を解放 ・ dict-add : キーとメッセージを辞書に追加 ・ dictsearch-exact : キーに完全に -- - ・致する項目を検索 ・ dict-search 」 ongest : キーにもっとも長く一一致する項 目を検索 ・ dict-traverse : 辞書の各メッセージごとになんらかの 処理を実行 Dict 型の実体は、 struct —Dict { DictNode *root ; という、たんにメンバーに木の根をもつだけの構造体であ る。さきはど説明した dictnode-* という関数はすべて、 dict. c のなかでファイル内のローカルなスコープしかもた ない static な関数として宣言されている。したがって、 dict. c の外からは dictnode-* は呼び出せす、かならす dict ー * 関数を用いて辞書の操作をおこなわなければなら このように内部の実装と外向きの API を分離しておけ ば、内部のデータ構造を簡単に交換できる。事実、リスト UNIX MAGAZINE 2002.7 いうファイル名で含まれている。 8 GLib のリストによる実装は、 chatty の / ヾッケージに dict-list. c と もできる。 おけば、内部のデータ構造を実行時に重加勺に交換すること 作って実験をおこなった 8 。また、内部の実装を分離して ために、内財冓造を GLib のリストに交換した chatty を で辭書を実装したときにどの程度の性能が出るかを調べる
プログラミング・テクニック his—want—state—is—will(option) ) 0 & & his—state-is-will(option)) Ⅱ if ( (do—dont—resp Coption] if (init) { int option, init ; send—do (option , init) void 図 4 send-do 関数 57 return ; 2 つ送り出していた場合には、さらに要求回数をデクリメ ントします。 * if ( (want—resp 0 ) & & (new-state ! = send want_state ; want_resp 十十 ; else want_state = new_state; if (ok_to—switch—to new_state) want-state)) { else ここではつねに DO コマン オプションです。そのため、 TELOPT-TM はタイミングを示すマークとして用いる この部分ではすこし特殊な処理がおこなわれています。 do—dont—resp [option] + + ; set—his—want_state_will(option) ; set—his—want_state_wont (option) ; if (option = = TELOPT-TM) 文で関数を終了している部分です。 かると思います。図 4 は、何もしない場合にます return のアルゴリズムと同様の処理をおこなうことになるのが分 1 です。 init 刎直か通常は 1 になっているため、さきほど 引数の option か、疋したいオフションで、 init は通常は DO コマンドを送るための関数が send-do です ( 図 4 ) 。 の要求を送ります。 い場合には、 want-resp をインクリメントしてこちらから に want-state を new-state にします。受け入れられな 査し、受け入れられる要求であれはその ACK を返すため はありません。そオび丿汐 P ) 場合には、受け取った要求を検 しい状態か要求した状態と等しい場合にも、何もする必要 答が返ってくるまで待っため、ここでは何もしません。新 ションに関する要求を送っていることになるので、その応 この段階で要求回数が 0 でなければ、はかにもこのオプ * my—state = new—state; UNIX MAGAZINE 2002.7 ドで送れるように工夫しています。 こまででデータが用意できたので、以降は実際にデー タを送出する部分になります。 (void) sprintf (nfrontp , , - (char *)doopt , option) ; nfrontp + = sizeof (dont) DIAG(TD_OPTIONS, printoption("td: , option) ) ; S end dO " データの送出は別の場所でおこない、この部分ではデー タをネットワークに送出するためのバッフアに追加して います。ここで、 doopt を処理しているのに、 dont の 文字数を使って長さを指定しています。システム上のはか の state. c ファイルをみると、この部分はデータ送出用 の関数に任せており、 doopt を出力するときはもちろん doopt の長さ ( 正確には、 doopt を使って生成した文字 列の長さ ) を利用しています。 一方、相手から WILL コマンドを受け取ったときには willoption 関数か呼び出されます。 void willoption (option) int option; この関数への引数は、どのオプションに対する WILL コマンドかを示す値です。 こちらも、さきほどのアルゴリズムを忠実に実現してい ます。 if (do—dont—resp [option] ) { do—dont-resp [option] ー his-state-is—will(option) ) if (do-dont-resp [option] & & do—dont—resp [option] ー 91 求を満たすことができるかどうかを順に詩ヾていきます。 さらに、実際に要求されたオプションにもとづいて、要
ー横着 ] プログラミング 6 図 9 dictnode-traverse 関数 static void dictnode—traverse (DictNode *node , DictTraverseFunc traverse_func , void *data) (node-> key & & node->value) traverse—func (node->key , node—>value , data) ; if if if (node->child) dictnode—traverse (node—>child, (node->next) dictnode_traverse (node—>next , traverse_func, traverse , data) ; data) ; . /dict—test —p chatty—dict. ja ー sort > x ( 1 ) テストの自動化 でよく、ソースコードを変更するたびに頻繁にテストして た。コマンドラインから、、 make check" と実行するだけ なる。そこで、 chatty ではテストを自動化することにし は、いくら大切だと分かってはいてもついつい怠りがちに う必要がある。しかし、いちいち手イ乍業でテストするので バグ昆入しやすい。したがって、慎重にテストをおこな どい条件分岐や再帰的な関数呼出しがおこなわれるため、 データ構造として木を扱うようなプログラムでは、きわ のような機能をもっている。 するためのプログラム dict-test を作成した。 テストの自動化にあたり、辞書に関するコードをテスト ok . /test—2 . /test—l ' /home/satoru/work/chatty/tests ' make [ 1 ] : 入りますディレクトリ % make check も苦にならない。 これは、次 UNIX MAGAZINE 2002.7 のようにすればいい。 と完全に同しかどうかを謌・ヾるには、 1 の機能を使って次 たとえば、辞書に読み込まれた内容がもとの辞書のもの ク・テストをおこなう。 4. 上記の 3 つの処理の性能を評価するためのべンチマー 3. 標準入力から指定されたキーを用いて辞書を本する。 くおこなえるかをテストする。 2. 辞書に登録されているすべてのキーに対して検索が正し 出力する。 1. 辞書に登録されているすべての項目を、辞書を探索して % sort chatty—dict ・ ja > y % cmp x y & & ech0 0k ok ( 3 ) 各行の意味は以下のとおりである。 ( 1 ) 読み込んた辭書の内容をソートして x に出力 ( 2 ) もとの辞書をソートして y に出力 (3)x と y の内容か完全に同しかどうかを上交 tests ディレクトリに置かれているテスト用シェルスク リプト test-l と test-2 では、この dict-test を用いて各 種のテストをおこなう。テストの自動化とその手法につい ては、『プログラミングイ料去』 [ 2 ] や『リファクタリング』 [ 3 ] に詳しい。 chatty は小さなプログラムなので使わなかったが、 au- t 。 t 。 ols9 を用いてソフトウェア開発をおこなう場合は、 automake のテスト自動化の枠組みを利用すると便利で ある。 メモリリークの検出 chatty では、重加勺にメモリを割り当てながら木を構築 していく。このようにして作成した再帰的なデータ構造 は、メモリリーク ( メモリの解放し忘のを起こしやすい。 メモリリークを検出するツールは、 ccmalloc10 、 Mem- profll 、 rnpatr0112 、 dmalloc13 などいろいろあるが、私 9 autoconf 、 automake 、 libtool のこと。最近のフリー・ソフト ウェアは、 autotools を利用することが多い。詳しくは、 http:// sources.redhat.com/autobook/を参具駕 127 13 http://dmalloc.com/ 12 http://www.cbmamiga.demon.co.uk/mpatrol/ http://people.redhat.com/otaylor/memprof/ ccmalloc/ 10 http://www.inf.ethz.ch/personal/biere/projects/ 11
連載 / IPv6 の実装ー⑨ 重複アドレス検出のための近知の受信 近隣通知は nd6-na-input() で処理されます。 1407 1408 1409 1410 1412 static void nd6—dad—timer (ifa) struct ifaddr *ifa; struct in6—ifaddr *ia = (struct in6—ifaddr *)ifa; 790 791 792 793 794 if (ifa & & ( ((struct in6—ifaddr *)ifa)—>ia6—f1ags & IN6_IFF_TENTATIVE) ) { nd6—dad—na—input (ifa) ; goto freeit; 上記は関数の一部です。受信した近 謝凾知の対象アドレスが自ノードに割り当てられているア ドレスで、かつ重複検出中 (IN6-IFF-TENTATIVE フ ラグが付いている ) の場合、 nd6-dad-na-input ( ) へ処理 を移します。 1657 static VOid 1658 nd6—dad—na—input (ifa) ifa は重複検出をしているアドレスへのポインタです。 1 , 412 行目では、 ifaddr 構造体で渡されるアドレス情報 を in6-ifaddr 構造体にキャストし、 IPv6 形式のアドレ スとして変数 ia でアクセスできるようにします。 1426 dp = nd6—dad—find(ifa) ; nd6-timer() にはアドレスの情報しか渡されないので、 重複アドレス検出の情報かオ褓内されている dadq 構造体を nd6-dad-find() でオします。 if (ia—>ia6—f1ags & IN6—IFF—DUPLICATED) { 1431 1659 1660 { struct ifaddr *ifa; 1436 1437 } goto done ; ifa は重複検出中のアドレスへのポインタです。 1666 dp = nd6—dad-f ind(ifa) ; 1667 if (dp) 1668 dp—>dad—na—icount + + ; アドレスの重複検出か完了しており、すでにアドレスに 重複を示すフラグカ寸いている場合には、重複アドレス検 出の手続きを終了します。 1447 if (dp->dad-ns—tcount > dad—maxtry) { アドレスに対応する dadq 構造体のエントリを検索し、 dad-na-icount の値を増やします。 dad-na-icount は重 複アドレス検出中に受信し丘凾知の数です。 1671 1672 } nd6-dad—dup1icated(ifa) ; 1451 1452 1453 1454 1455 1456 } TAILQ_REMOVE (&dadq, (struct dadq *)dp, dad-list) ; 、 free(dp, M—IP6NDP) ; dp = NULL ; IFAFREE(ifa) ; goto done ; 重複アドレス検出のために送信した近隣要請に対応して 近隣通知が送られてきたので、このアドレスは重複してい ることになります。 nd6-dad-duplicated() でアドレスに 重複していることを示すフラグを付け、以後の対応をシス テム管理者に委ねます。 タイマー里 重複アドレスの検出は、特定回数の瓲舞要請を送り、対 応する近隣通知がないことを石忍するという処理になりま す。重複検出中のアドレスに対して定期的に近隣要請を送 信するためのタイマー処理は、 nd6-timer() 関数で実装さ 84 れています。 dad-ns-tcount は nd6-dad-ns-output() を呼び出し た回数です。この回数が dad-maxtry を超えたら重複ア ドレス検出を停止します。 dad-maxtry の値は 15 です。 対応する dadq 構造体のエントリをリストから外します。 dad-ns-ocount は実際に nd6-ns-output() を呼び出 した回数です。この値が重複アドレス検出のための最大 近隣要請回数である dad-count に達するまでは、定期的 に近隣通知を送信する必喫があります。 put() で近隣通知を送信し、 nd6-dadstarttimer() で次 のタイムアウトを設定します ( 図 13 ) 。 1466 } else { UNIX MAGAZINE 2002.7
連載 /JavaServer Pages— 図 5 木星 " を選んだときの JSP ページの表示個遊券システ ムのサンカレ ) ファイル ( E ) 編集 ( 印表示検索 0 ) ジャンプ 0 ) ブッりマーり ( 印タスク (I) へ ) に ( 旦 ) 木ーム検常 1 プマ、グ、を猷聞を橋”。、新着、お勧め - 、メンバーズ 大午ミこ周返券ー Netscape 6 [ 発券内容 ] 地球つ太陽系内 了 44 砂厂 経由 : jupiter く input く input く input く input type= type= type= type= " checkbox" name= value="venus" > 金・星 . " checkbox" name= 'route" value="mars"> 火星 - " checkbox" name= jupiter"> 木星 " checkbox" name= value= 'saturn"> 土星 . route" というチェックポックスで、、、火星 " と、、十蹕「の 2 つを 選んだとします。この場合、 JSP ページを呼び出すとき の URL は、 http : //tomcat : 8080/unimaga/tour. j sp?route=mars& route=saturn となり、 JSP ページには route パラメータが 2 っ渡さ れます。 JSP 工ンジンは、フォームから渡された r 。 ute パラメータを ticket オプジェクトに設定しようとします。 しかし、 setRoute() メソッドはプロバティを 1 つしか 受け町せ・しないので、 route ノヾラメータのうちの 1 つだけを ticket オプジェクトのプロバティに設定します。 96 pe getPropertyName(int index) e value public void setPropertyName(int index, ・イ嬲リの要素にアクセスするメソッド 定めています。 ティのことで、 JavaBeans のイ土様では以下のメソッドを ます。インデックス付きプロバティとは配列形式のプロバ 使えは、複数の値か渡されるパラメータの問題を鮹夬でき インデックス付きプロバティ (indexed property) を インテックス付きプロバティ ラメータに複数の値か渡されることがあります。 ォームの部品に同し名前を付けたときには、同じ名前のパ 値を選べる選択リストや、意図的に、あるいは誤ってフ チェックポックスのはかにも、一嚇甫のなかから複数の ・酉改」全体にアクセスするメソッド public void setPro 〃 er 、匆ル ame ( を e values public type[] getPropertyName() これらのうち、 JSP のタグから利用できるのは配列全 体にアクセスする setter メソッドです。 JSP の jsp:set- UNIX MAGAZINE 2002.7 CLjava. lang. String;@fd1810 表示するため、図 7 の、 によって文冽として表現できる形式に変換して画面上に 文字列配列 . toString() ように文字列酉改」カ区されたときも、 ると、文字列に変換してから表示します。 getRoute() の すためです。 jsp:getProperty は文字列以外の値カされ ticket クラスの getRoute() メソッドか文字列の酉改」を返 これは、 jsp:getProperty タグによって呼び出される 表示にならないはすです。 実行してください。図 7 のように、経由地か期侍どおりの 入れ替えて、周遊券システムのサンフ。ルの JSP ページを ticket JavaBean をインデックス付きプロノヾティ版に インデックス付きプロバティ直の取得 存されます。 の数のデータが、 ticket クラスの route 変数 ( 酉改 J) に保 この処理によって、チェックポックスで〕尺した任意 数の酉冽の中身を酉冽 route にコピーします。 ます。次に System ・ arraycopy() メソッドを使って、引 列と同し要素数の文字列酉冽を作り、 route 変数に設定し さい。このメソッドでは、最初に引数として受け取った配 ロバティを設定する setRoute() メソッドに注目してくだ をインデックス付きプロバティにしたものです。 route フ 図 6 は、図 4 の ticket クラスを書き換えて、、、 route' ておくとよいでしよう。 よって扱いを変えたいときは、個別のプロバティに保存し ィがイリです。逆に、同レヾラメータ名ではあっても値に めに保存しておきたいときは、インデックス付きプロバテ 区別する必要がないイ寺定の数のパラメータをひとまと ます。 を、配列としてインデックス付きプロバティに設定でき Property タグは、チェックポックスで選んだパラメータ
連載 JavaServer pages—@ 図 2 入力醯のフォームのソースコード個遊券システムのサンカレ ) く ! DOCTYPE HTML PUBLIC "—//W3C//DTD HTML 4.0 Transitiona1//EN"> く html > く head> く meta http—equiv="Content—Type'i content="text/html ; charset=EUC¯JP"> く center> [ 経由地入力画面 ] く /center> く p> く body> く /head> く title > 太陽系ミニ周遊券く / title > く form action="tour. JSP"> く input type く input type= く input type= く input type= く input type= く /form> く /body> く / 五 tml > = ” checkbox" name= " checkbox" name= 'route' "checkbox" name="route" " checkbox" name= " submit'l value= route' " 選択わ value="venus " > 金星 . value="mars"> 火星 . jupiter"> 木星 value="saturn"> 土星 . ←フォームで選択した結果は t 。 ur. jsp へ送られる チェックポックスで選択する value= 図 3 JSP ページのソースコード ( 周遊券システムのサンカレ ) く %@ page contentType="text/htm1 ; charset=EUC-JP" % > く % page import="tour. * " % > く ! DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 Transitiona1//EN" > く html> く head> く meta http—equiv="Content—Type" content="text/html ; charset=EUC¯JP"> く title > 太陽系ミニ周遊券く / title > く /head> く body> く center> [ 発券内容 ] く /center> く br> ← ticket JavaBean を生成する く Jsp:useBean class="tour. / > く jsp: setproperty name="tck" property="route" / > ←チェックポックスで選択した値をプロノヾティに設定 経由 : く jsp:getproperty name="tck" property="route"' / > ← route プロノヾティの値を表示 地球太陽系内く br > く /body> く /html > 図 4 ticket JavaBean のソースコ package tour ; public class ticket { private String route public ticket ( ) { } ード個遊券システムのサンカレ ) ← r 。 ute プロバティの値を保持する変数 public void setRoute(String r) {route = r;} public String getRoute ( ) {return route ; } UNIX MAGAZINE 2002.7 ← setter メソッド ← getter メソッド 95
横 : ] プログラミング 6 図 8 端末掏コードを用いたメッセージ表示 口にオロ ロ ー、 1 % intf ' \ 033 ] 0 s \ a ' abc 図 6 キーを逆向きにした木 abc ロ 圍 ロ ロロ - ロ 00 ■住 ロロ -000 面国 0 watch—input (Dict *dict , RevBuf *revbuf , *str, int len) const Char int 1 ; 0 ; i く 1en ; fo て int ch = str [i] ; Char *message ; if (revbuf—full—p (revbuf ) ) revbuf—clear (revbuf) ; revbuf—add(revbuf , (h) ; message = dict—search—longest(dict' revbuf—top (revbuf) ) ; if (message) printf ( " \ 033 ] O;%s\a" , message) ; , \n , Ⅱ ch = if (ch revbuf—clear (revbuf) ; a a 図 7 revbuf revbuf top 入力 NUL 8 X watch-input 関数には、標準入力から入力された長さ len の文字列 str カヾ度される。通常、 watch-input 関数は ューザーが 1 文字入力するたびに呼び出されるため、 len の値はほとんどの場合 1 である。 for ループのなかでは、入力された文字を revbuf-add 関数で revbuf に追加し、 dict -search 」 ongest 関数で辞 書検索をおこなっている。検索に成功し、 message に値 が入っているときは、端末の制御コードを用いて printf 関数でメッセージをタイトノレヾーに表示する。 このような辞書の木を作っておけば、後ろから遡って辞 端末の制御コードを使えば、コマンドラインから次のよ 書検索をおこなうときに、木をⅡ→ a → rn → x と 1 段 うに実行してタイトノレヾーにメッセージを表示することも すっ探索を進めればよく、効率的である。 できる ( 図 8 ) 。 後ろから遡る検索をおこないやすくするために、 chatty % printf ' \ 033 ] O;%s\a' abc では入力された文字列も逆向きに言当求している。図 7 は、 chatty ではタイトルバーにメッセージを表示する処理 入力 x m a n を文字列バッファ revbuf に逆向きに言求し しか実装していないが、ユーサーが入力した文字列に応し ていく様子を示している。矢印で示した、、 t 。 p " は文字列 て言算機に喋らせたり、任意のコマンドを実行できればお の知頁を表すポインタである。 もしろいかもしれない。 入力の監視 逆向きの文字列パッファ 入力の監視は、逆向きの文字列バッファ revbuf を用い 逆向きの文字列バッファ revbuf は次の構造体で表現さ ておこなう ( 誌面の都合上、斤り返しています。以下 れる。 typedef struct { char str [BUFSIZ] ; top t0P t0P n a m X NUL void UNIX MAGAZINE 2002.7 124
連載 JavaServer Pages 図 8 JSP ページのソースコード ( インデックス付きプロバティ版 ) く % page contentType="text/htm1 ; charset=EUC-JP" % > く %@ page import="tour. * ” % > く ! DOCTYPE HTML PUBLIC "—//W3C//DTD HTML 4 . 0 Transitiona1//EN" > く html> く head> く meta http—equiv="Content—Type" content="text/html ; charset=EUC¯JP"> く title > 太陽系ミニ周遊券く / title > く /head> く body> く center> [ 発券内容 ] く /center> く br> く Jsp:useBean id='ttck" class="tour. ticket" / > く」 sp : setproperty name="tck" property="route" / > 地球太陽系内く br > 経由 : く % ・←スクリプトレット内で getter メソッドを呼び出す String route ロ . = tck. getRoute() , for (int i = 0 ; i く route. length-l; i + + ) { if (route ! = null) { く % く % く /body> く /html> く % = route Ci] % > く % = route Croute. length-l] % > 図 9 経由地を正しく新した例 ファイ井の編集 ) 表示 ( u ) 検索境 ) ジャンプ 0 ブックマゆ⑧タスり (I) ヘルプ旧 ) 六系ミ : 周返券 - ル叩う 。」ホーム検索朝プマーク、いール“・新着、お動め、メンバーズ ロ 9 、第ュメント : 完了 ( 2 由 : mars ー saturn 地球つ太陽系内 [ 発券内容 ] ←経由地を 1 っすっ表示する ←最後の経由地の後ろには、一 " を付けない 列として受け取ります。工ンコーディングの判定か誤って は、フォームに設定された複数の文字列を String 型の配 public void setRoute(String r ロ ) ングを確実に判定する手段はありません。そのため、判定 を誤ると文字化けした文字列カワ。ロバティに設疋されてし まいます。 同しことは、日本語にかぎらすョーロッパの言語や中国 語など、 ASCII 以外で表現された文字列についてもいえ ます。ここでは、日本言韶 ) 文字化けを防ぐ力法を説明しま すが、ほかの言語でも同し手法カ硬えます。 工ンコーディングの変換 図 6 に示した ticket JavaBean の setRoute() メソ 98 いる場合は、引数の配列に文字化けした文字列か叡疋され ているので、要素をひとつひとっ取り出して正しいエンコ ーディングに変換すれば正しく表示できます。 ェンコーディングの変換は次の手順でおこないます。 1. 文字列をバイト列に変換する。 2. バイト列を文字列に再変換する。 文字列をバイト列に変換するには、 String クラスの getBytes() メソッドを使います。次のコードは、文字 列 strtng を指定されたエンコーディング e れ co 市に従っ てバイト列に変換します。 s ツれ 9. getBytes ( e れ CO 市れの , byte bytes ロ Tomcat では、フォームの文字列を ISO ー 8859 ー 1 (Lat- in-l) 工ンコーディングと解釈することが多いので、 en- UNIX MAGAZINE 2002.7