Lambdaカクテル

集団への盲従を激しく嫌う

Common Lisp困りどころ その2: ClozureCLのファイルパスのエンコーディング

台風の風がするどい.実家の九州では珍しくもないことだが,関西でこれほどの風が吹くのは久しぶりだ.

さて今回ClozureCLの日本語処理でハマったのでメモしておく.

シチュエーション

  • OSはFreeBSD 11.1
  • ファイルシステムはZFS
  • 処理系はClozureCL 1.11(roswellでインストールした.ccl-bin/1.11)
  • ファイルリストをuiop/filesystem:subdirectoriesまたはuiop/filesystem:directory-filesで取得した.
  • これをWebフレームワークであるcaveman2で表示しようとしたところ,文字化けが発生した.
  • プロキシとしてnginxを挟んでいる.

問題調査

まずnginxの問題を疑ったが,問題ないことがわかった.caveman2が表示するテンプレートに日本語を混ぜたところ,問題なく表示できたからである.

またファイル名自体が文字化けしているのではないかと疑ったが,問題ないことがわかった.sshによって直接ファイルリストを取得してみたが,正常に表示されたからである.

ここで文字化けしている文字列がどのように化けているのか確認するために 文字化け解読ツール「もじばけらった」 を使用した.すると,これをlatin1で再度エンコードしてUTF-8でデコードすると正常に表示できることがわかった.

「latin1 clozurecl」でググっていると以下のようなページに辿り着いた.

t2ru.hatenablog.jp

どうもClozureCLに共通する問題らしい.もしやと思い,ファイルリストを取得する箇所に以下のようなコードを挿入した.

;; before
(defvar pathstr "/foo/bar/")
(defvar path (pathname pathstr))

;; 文字列がほしいのでenough-namestringを使う
;; namestringでよかったかも?
 (mapcar #'enough-namestring (directory-files pth))
 (mapcar #'enough-namestring(subdirectories pth))
;; after

;; 変換関数
(defun ensure-encoding-in (s)
  ; cclの場合にのみ,一度文字列をlatin1でオクテット列に戻し,utf-8でデコードしなおす
  ; '外の世界'から入ってきたパス名に適用する
  #+ccl
  (setf s (babel:octets-to-string (babel:string-to-octets s :encoding :latin1) :encoding :utf-8))
  s)
(defun ensure-encoding-out (s)
  ; cclの場合にのみ,一度文字列をutf-8でオクテット列に戻し,latin-1でデコードしなおす
  ; '内の世界'から出ていくパス名に適用する
  #+ccl
  (setf s (babel:octets-to-string (babel:string-to-octets s :encoding :utf-8) :encoding :latin1))
  s)

(defvar pathstr (ensure-encoding-out "/foo/bar/"))
(defvar path (pathname pathstr))

(mapcar 'string #'ensure-encoding-in (mapcar #'enough-namestring (directory-files pth)))
(mapcar 'string #'ensure-encoding-in (mapcar #'enough-namestring (subdirectories pth)))

すると正常にファイル一覧を得ることができた.よかったですね.

なんでやねん

どうしてこうなるのか調査しようとしたが,自分の やり方が不十分なのか,clozureclがおちゃめなのかまだ分かっていない.

そもそもccl:*default-file-character-encoding*とかをちゃんと設定したら動くんちゃう?という気もするが,今日は気力が尽きてしまった.