引用元:https://www.ioccc.org/2015/duble/prog.c
審査員・作者による説明:https://www.ioccc.org/2015/duble/hint.html
動作
Unicodeの点字を使って手書きフォントを表示する。
$ gcc -o prog prog.c
$ ./prog
>
プロンプトが表示されるので、メッセージを入力する。
$ ./prog
> Hello, world!
改行を打つと、点字が点ごとに印字されて、手書きされている風のアニメーションとして表示される。
次のようにprog.cを入力として与えると”segmentation fault”が点字で表示される。
$ ./prog < prog.c
環境変数DRAFT
を設定して起動すると、速くなる。
ただしフォントが少し粗くなる。
解説
対応している文字はA
-Z
と.
:
,
;
!
?
。大文字と小文字の区別はしない。
コード形状は”LOCK”と書かれた錠前と、 末尾にわかりやすくmain()
が置いてある。
これは、コード中のどのパートが難読化されているか(つまりほぼ全部)を示しているらしい。
なお、このmain()
の定義は明らかに偽物で、#define main()
によって上書きされている。
プリプロセスを通すと、肥大化してわけがわからなくなる。
Q_
とmu
というマクロが邪悪で、Q_(...)
は中身を125回繰り返すものになっている。
Q({)
やQ(})
はインデントを破壊するためだけなのでQ_
を取り除いて問題ないが、Q_(O_o)
は取り除くと動かなくなる。
if
文や三項演算子は使わず、再帰とショートカット演算子でフローが書かれている。
ただし、慎重に展開すればQ9-- || ...;
というようなコードの列であることがわかるので、switch(Q9){...}
に直すことは比較的簡単。
prog.cを与えたときに”segmentation fault”と出るのは、#
という文字を特別扱いしているため(prog.cは#include
から始まることに注意)。
echo "foo#bar" | ./prog
と実行すると”foosegmentation fault”と出るのでわかりやすい。
こういう工夫はとても良いと思う。
DRAFTモードは、lrand48()
を使ってわざと出力を粗くする。
ただしsrand()
していないので、結果は固定になっている。
普通に起動した場合はプロンプトが出るが、標準入力にリダイレクトして起動した場合は出ない。
通常これはisatty()
を使って判定されるが、このプログラムはisatty()
を使っていない。
どうやってこの挙動を実現しているか、ということがクイズになっている。
答えを言うと、プロンプトは標準入力にwrite()
されている。
パイプを通さず普通に起動した場合、標準入力への書き込みは標準出力に出るが、リダイレクトの場合は無視される、という挙動を活用したハック。
なおコード読解する際は、dup()
やdup2()
のトリックによって、標準入力が1番、標準出力が0番というようにファイルディスクリプタが差し替えられていることに注意。
フォントデータはcodes.txtに説明がある。