デコード
ホームページなどのフォームから入力された日本語文字列をデコードしたいのです。けど、gnu-Smalltalkにはそんなメソッド用意されてないみたいっす。なので、Squeakの unescapePercentsWithTextEncoding: を参考にして(ぶっちゃけパクリました)作ってみた。
!String methodsFor: 'converting'! urlDecode | inData str oldPos pos patt c asciiVal | str := ReadWriteStream on: ''. oldPos := 1. patt := '%([a-fA-F0-9][a-fA-F0-9])' asRegex. inData := self copyReplaceAll: '+' with: '%20'. [pos := (inData indexOfRegex: patt startingAt: oldPos ifAbsent: [ (oldPos <= inData size) ifTrue: [ str nextPutAll: (inData copyFrom: oldPos to: inData size). ^str contents ] ifFalse: [^str contents]. ]) first. pos > 0 ] whileTrue: [ str nextPutAll: (inData copyFrom: oldPos to: pos - 1). c := inData at: pos. (c = $% and: [pos + 2 <= inData size]) ifTrue: [ [c = $%] whileTrue: [ asciiVal := (inData at: pos+1) asUppercase digitValue * 16 + (inData at: pos+2) asUppercase digitValue. asciiVal > 255 ifTrue: [^self]. str nextPut: (Character value: asciiVal). pos := pos + 3. (pos <= inData size) ifTrue: [ c := inData at: pos] ifFalse: [c := nil]. ]. oldPos := pos. ] ifFalse: [ str nextPut: c. oldPos := pos + 1 ]. ]. !
なんだかすごく読みづらいコードですね、ごめんなさい。
んで、次の様なテストCGIで試してみた。
#!/usr/bin/env gst -Q "testform.cgi" | html inData | html := 'Content-Type: text/html; charset=UTF-8 <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>gst てすとふぉーむ</title> </head> <body> <h1>gst てすとふぉーむ</h1> <form action="testform.cgi" method="POST" accept-charset="UFT-8"> <textarea name="text" rows="10" cols="40"></textarea><br> <input type="submit" value="入力"> </form> <pre> %1 </pre> </body> </html> '. ((Smalltalk getenv: 'CONTENT_LENGTH') = nil) ifTrue: [stdout nextPutAll: html % {''}] ifFalse: [ inData := ((stdin nextAvailable: (Smalltalk getenv: 'CONTENT_LENGTH') asInteger) tokenize: '=') at: 2. stdout nextPutAll: html % {inData urlDecode} ]. !
環境に合わせて charset をEUC-JP、Shift_JIS、UTF-8 の三つで試してみましたが、問題無くデコードできました。それよりも問題なのは、 stdin でした。最初はローカル内のapache2系を使って stdin next: というふうに読み出していて、特になんの問題も無かったので、自分が借りてるレンタルサーバーで使われているapache1.3系でテストしてみると、何故かエラーです。エラーの詳細を調べてみると、1023バイト以上のPOSTデータを正しく読み出せていないのが原因でした。何故こんなエラーが出るのか解からないまま、とりあえず、試しに stdin contents としてみたらちゃんと読み出せた。けど、この方法だと今度はapache2系でnilエラーが出るという摩訶不思議な現象。色々試した結果、stdin nextAvailable:だとどのバージョンのapacheでもちゃんと読み出せるようなので、とりあえずOK.(いいのか?)