Quine
Quine とは、自分自身のソースコードを出力するプログラムである。
今回の戦略
今回は、以下の方針で実装した。
- ソースコードの前半部分 (エンコードした文字列より前) をデコードして出力する
- ソースコードの前半部分をエンコードした文字列を出力する
- ソースコードの中間部分 (前半部分をエンコードした文字列と後半部分をエンコードした文字列の間) を出力する
- ソースコードの後半部分をエンコードした文字列を出力する
- ソースコードの後半部分 (エンコードした文字列より後) をデコードして出力する
「エンコード」には、Base64 を用いた。
実装
#include <stdio.h>
void decodeb64(const char* b64) {
int byte = 0, bit = 0;
while (*b64 != '\0') {
byte <<= 6;
bit += 6;
if ('A' <= *b64 && *b64 <= 'Z') byte += *b64 - 'A';
else if ('a' <= *b64 && *b64 <= 'z') byte += *b64 - 'a' + 26;
else if ('0' <= *b64 && *b64 <= '9') byte += *b64 - '0' + 52;
else if (*b64 == '+') byte += 62;
else if (*b64 == '/') byte += 63;
if (bit >= 8) {
bit -= 8;
putchar(byte >> bit);
byte &= (1 << bit) - 1;
}
b64++;
}
}
int main(void) {
const char* front = "I2luY2x1ZGUgPHN0ZGlvLmg+Cgp2b2lkIGRlY29kZWI2NChjb25zdCBjaGFyKiBiNjQpIHsKICAgIGludCBieXRlID0gMCwgYml0ID0gMDsKICAgIHdoaWxlICgqYjY0ICE9ICdcMCcpIHsKICAgICAgICBieXRlIDw8PSA2OwogICAgICAgIGJpdCArPSA2OwogICAgICAgIGlmICgnQScgPD0gKmI2NCAmJiAqYjY0IDw9ICdaJykgYnl0ZSArPSAqYjY0IC0gJ0EnOwogICAgICAgIGVsc2UgaWYgKCdhJyA8PSAqYjY0ICYmICpiNjQgPD0gJ3onKSBieXRlICs9ICpiNjQgLSAnYScgKyAyNjsKICAgICAgICBlbHNlIGlmICgnMCcgPD0gKmI2NCAmJiAqYjY0IDw9ICc5JykgYnl0ZSArPSAqYjY0IC0gJzAnICsgNTI7CiAgICAgICAgZWxzZSBpZiAoKmI2NCA9PSAnKycpIGJ5dGUgKz0gNjI7CiAgICAgICAgZWxzZSBpZiAoKmI2NCA9PSAnLycpIGJ5dGUgKz0gNjM7CiAgICAgICAgaWYgKGJpdCA+PSA4KSB7CiAgICAgICAgICAgIGJpdCAtPSA4OwogICAgICAgICAgICBwdXRjaGFyKGJ5dGUgPj4gYml0KTsKICAgICAgICAgICAgYnl0ZSAmPSAoMSA8PCBiaXQpIC0gMTsKICAgICAgICB9CiAgICAgICAgYjY0Kys7CiAgICB9Cn0KCmludCBtYWluKHZvaWQpIHsKICAgIGNvbnN0IGNoYXIqIGZyb250ID0gIg";
const char* back = "IjsKICAgIGRlY29kZWI2NChmcm9udCk7CiAgICBmcHV0cyhmcm9udCwgc3Rkb3V0KTsKICAgIGZwdXRzKCJcIjtcbiAgICBjb25zdCBjaGFyKiBiYWNrID0gXCIiLCBzdGRvdXQpOwogICAgZnB1dHMoYmFjaywgc3Rkb3V0KTsKICAgIGRlY29kZWI2NChiYWNrKTsKICAgIHJldHVybiAwOwp9Cg";
decodeb64(front);
fputs(front, stdout);
fputs("\";\n const char* back = \"", stdout);
fputs(back, stdout);
decodeb64(back);
return 0;
}
動作確認
以下のようなコマンドでプログラムを実行し、ソースコードと出力が一致している事を確かめた。
gcc -Wall -Wextra -O2 -o main main.c
./main > out.c
sha256sum *.c
システムの仕様に合わせるため、実際のコマンドは若干異なる。
以下の出力が得られ、ソースコードと出力が一致している (確率が非常に高い) ことがわかる。
0aeb393850515f49cc475946353b58ec14432eb9bc949cd25355e499b2000f63 main.c
0aeb393850515f49cc475946353b58ec14432eb9bc949cd25355e499b2000f63 out.c
おわりに
今回のプログラムを書いた後に Wikipedia を見たら、もっと賢い実装例があるやんけ……orz