たまに書きます。

気になって調べたことを書いていきます。

大昔のgccのプリプロセッサに関する、極めてどうでもよい話

先日、酒を飲みながらgccだかなんだかのコンパイラの話をしていたときに、ふと、「そういえば昔のgccプリプロセッサ#pragmaディレクティブを見つけると、コンパイルを中止してゲームを起動するとかいうのがあったよな」と思い出した。 この元ネタは、もう絶版だけど、「エキスパートCプログラミング」という本に書いてある。

エキスパートCプログラミング―知られざるCの深層 (Ascii books)

エキスパートCプログラミング―知られざるCの深層 (Ascii books)

C/C++において、#pragmaは、ヘッダの二重インクルードを防止するために、伝統的な

    #ifndef GUARD
    #define GUARD
    ...
    #endif

を使用したインクルードガードに変わって#pragma once のように用いられることが多い。しかし、pragmaに続くキーワードやその動作は下記のページにもあるように、処理系ごとに定義が委ねられている。

C言語/前処理指令 - Wikibooks

当時のgccはどうも#pragmaをいけ好かないものとしていたようで、「処理系ごとに決められた動作」として、ゲームを起動するというようなことをやっていたらしい。

せっかくなので、gccの古いソースコードを掘り返してみる。 アーカイブへの直リンクは

http://ftp.tsukuba.wide.ad.jp/software/gcc/old-releases/gcc-1/

さて、まずは一番古そうなのをということで、verion 0.9 (gcc-0.9.tar.bz2, 1987/03/22リリース)を覗いてみる。 このなかの、cccp.cというファイルを見てみると、2401行目付近に、

/*
 * the behavior of the #pragma directive is implementation defined.
 * this implementation defines it as follows.
 */
do_pragma ()
{
  close (0);
  if (open ("/dev/tty", O_RDONLY) != 0)
    goto nope;
  close (1);
  if (open ("/dev/tty", O_WRONLY) != 1)
    goto nope;
  execl ("/usr/games/hack", "#pragma", 0);
  execl ("/usr/games/rogue", "#pragma", 0);
  execl ("/usr/new/emacs", "-f", "hanoi", "9", "-kill", 0);
  execl ("/usr/local/emacs", "-f", "hanoi", "9", "-kill", 0);
nope:
  fatal ("You are in a maze of twisty compiler features, all different");
}

というように、このころのコードには確かにゲームを起動するコードが仕組まれている。ちなみに、私はhackもrogueも名前だけは知っていたが、起動して遊んだことはないので何するゲームなのかよく知らない*1。 なるほどexeclはそのプロセスをそのまま呼び出すコマンドで置き換えるから、より新しいものから起動を試してみて、見つからなかったらrogueというように順々に探していくわけだ。 ちなみに、コンパイルができなかったために、本当にpragmaを見つけるとゲームが起動するのか、手元で確かめてはいない。というのも、do_pragma()という関数自体が、関数ポインタを介して呼ばれているようで、必ず呼ばれるものか、いまいち確信は持てていないためだ。

つぎに、gcc-1.21(gcc-1.21.tar.bz2, 1988/05/01リリース)を見てみる。 すると、同じくcccp.cの2993行目付近

#if 0
/* This was a fun hack, but #pragma seems to start to be useful.
   By failing to recognize it, we pass it through unchanged to cc1.  */

/*
 * the behavior of the #pragma directive is implementation defined.
 * this implementation defines it as follows.
 */
do_pragma ()
{
  close (0);
  if (open ("/dev/tty", O_RDONLY) != 0)
    goto nope;
  close (1);
  if (open ("/dev/tty", O_WRONLY) != 1)
    goto nope;
  execl ("/usr/games/hack", "#pragma", 0);
  execl ("/usr/games/rogue", "#pragma", 0);
  execl ("/usr/new/emacs", "-f", "hanoi", "9", "-kill", 0);
  execl ("/usr/local/emacs", "-f", "hanoi", "9", "-kill", 0);
nope:
  fatal ("You are in a maze of twisty compiler features, all different");
}
#endif

というように、コメントアウトされてしまっている。しかも「なんだか使い道が見えてきた(意訳)」という言い訳つきである。上記のwikibooksによると、「処理系が認識できないプラグマは無視する」と書いてあるが、この段階ではコメントにもあるように、プリプロセッサでは無視しているようだ。

エキスパートCプログラミングという本自体、1996年に出版されていることを考えると、この本が「昔」というのだから「昔のさらに昔」ということで本当に初期の一瞬だけの話だけだったようだ。ちなみに、私は1990年生まれなので、生まれる前にはもうpragmaは「ちゃんと使われていた」ことを今更知った次第である。

なお、gcc-0.9のビルド周辺に関しては、下記のページでビルドの試行錯誤などがされている。

virtuallyfun.superglobalmegacorp.com

終わり