lemonade でマルチバイト文字がコピーできなかった現象とその顛末

TL;DR $LANG 設定しろ。


某月某日。私はとある問題に頭を抱えていた。

「なんでマルチバイト文字を含む文字列だけコピーできないんだ!」

私は lemonade を使い、ssh 先から文字列をコピーしようとしていた。

lemonade については過去の記事に紹介を譲る。

ところが、マルチバイト文字を含む場合のみ、クリップボードが空になってしまうのだ。ASCII 文字だけならばなんの問題もないというのに。

最初は lemonade のバグを疑ったが、仮に報告をしたとして、現在 lemonade はメンテナを募集している状態だ。修正される可能性は低いだろう。何より困っているのは私自身だ。

「自分で調査するしかないか…」

lemonade は Go 言語で実装されている。Go 言語は不慣れだが、幸いにも勉強しようと思っていたところだ。既存のソフトウェアのソースコードを読むのは大変勉強になる。ある意味丁度いい。

原因を調査するために、ソースコードを変更してログを仕込む。しかし、私の lemonade サーバは Mac で動いており、以下のような設定でデーモンとして起動している。

~/Library/LaunchAgents/lemonade.server.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>lemonade.server</string>
  <key>KeepAlive</key>
  <true/>
  <key>ProgramArguments</key>
  <array>
    <string>lemonade</string>
    <string>server</string>
  </array>
  <key>RunAtLoad</key>
  <true/>
</dict>
</plist>
$ launchctl load ~/Library/LaunchAgents/lemonade.server.plist

このままでは標準出力に吐かれるログが見れない。いや、見る方法はあるのかもしれないが、そこまでは調べなかった。

ここはデーモンを止めて、コマンドラインから実行してしまうのがはやい。

$ launchctl unload ~/Library/LaunchAgents/lemonade.server.plist
$ lemonade server

これでログを見ることができる。

愚直に print デバッグを繰り返していると、とあることに気付く。

「あれ? …普通にマルチバイト文字送れてるぞ?」

そう、実はこの状態であれば lemonade はご機嫌に動作したのだ。

ここまで来ると原因が絞り込めてくる。この状態で動いているということは、lemonade のバグの線は消えたと言っていい。あとは環境の違いということになる。デーモンではダメで、手元のターミナルから直接実行だと動く。そして起きている現象は、マルチバイト文字に絡む…。

環境変数 $LANG 辺りが怪しいな」

そう思い立つと、適当に環境変数をファイルに出すプログラム sh -c 'env > env.txt' をデーモンに仕込んで、デーモン環境での環境変数を確認してみる。

思った通り、環境変数 $LANG が設定されていない。恐らくこれだろう。ということで、デーモンに環境変数を設定するように plist ファイルを以下のように書き換えた。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>lemonade.server</string>
  <key>KeepAlive</key>
  <true/>
  <key>ProgramArguments</key>
  <array>
    <string>lemonade</string>
    <string>server</string>
  </array>
  <key>RunAtLoad</key>
  <true/>
  <key>EnvironmentVariables</key>
  <dict>
    <key>LANG</key>
    <string>ja_JP.UTF-8</string>
  </dict>
</dict>
</plist>

これで動かしたところ、無事、マルチバイト文字のコピーに成功した。世界に平和が訪れた。

後で知ったが、launchctl setenv などでデーモン全体の環境変数を設定することも可能なようだ。こちらでも良さそうだが、試していない。