_ 完全復活とまではいかないまでも、まあ人並みに活動できるまでにはなおった。
_ で、とたんに昼飯も食えないほどの忙しさ。けっして仕事を溜めてたわけではなくて(いや、溜まってるけどデッドラインにはまだある)、突発的なトラブルがいくつも降ってきた。なんで狙ったように。
_ つーか、去年の秋に4台設置したサーバのうち、今日でディスク故障3台目ってどういうことよ。とんでもない大はずれ不良ロットをひいてしまった。
_ anarchy golfではいろんな計算機言語が使えるけど、使えない言語もある。というわけで、m4 で FizzBuzz(←言語じゃねーよ)。
define(`forloop', `pushdef(`$1', `$2')_forloop(`$1', `$2', `$3', `$4')popdef(`$1')')dnl define(`_forloop', `$4`'ifelse($1, `$3', , `define(`$1', incr($1))_forloop(`$1', `$2', `$3', `$4')')')dnl forloop(`i', 1, 100, `define(`f', ifelse(eval(i%3), 0, `Fizz', `'))f`'dnl ifelse(eval(i%5), 0, `Buzz', f, `', i) ')dnl_ fooloop マクロは GNU m4 の info にあるものをそのままコピペしただけ。ゴルフ的に短くしようとする努力はまったくしてないので、ロジックをいじらなくても、無駄な空白や `' を削ったり、マクロの名前を短くしたりするだけで100バイトぐらいは縮むはず。
_ referer 探知しますた。起動スクリプトを書き換えて $PATH を設定するのが正解。
_ 各種デーモンのうち、とくに子プロセスとして任意のコマンドを起動する可能性のあるものは、いらん環境変数を消してから起動しましょう。cron(各種スクリプトを起動する)、inetd(各種デーモンを起動する)、httpd(CGI を起動する)、MTA(.forward や .qmail からコマンドを起動する)など。
_ なんでかっつーと、デーモンが自力でクリアしないかぎり、起動したときに設定されていた環境変数が子プロセスにまで引き継がれるから。シェルから手で起動すると、$LOGNAME やら $REMOTEHOST やら $SSH_AUTH_SOCK やらのいらん環境変数がごてごてくっついてくる。子プロセスにそんないらん情報を漏らしちゃいかん。
_ $PATH がそのままだと、command not found になったり、 #!/usr/bin/env perlとかやってるスクリプトでは、/usr/bin/perl のつもりで /home/hoge/bin/perl が起動しちゃうなんてことがありうる。$LANG や $LC_MESSAGE などがそのままだと、何かのコマンド出力をパーズするようなスクリプトが出力メッセージの違いでコケることがありうる。$LD_LIBRARY_PATH なんて何が起きても不思議じゃない(どうしても必要であえて設定することもあるが)。必要な環境変数があれば、いったんすべてクリアした上で必要なものだけあらためて設定しなおして起動すべし。
あるいは、env - PATH=... とするように起動スクリプト自体を修正しておく。いらん環境変数を残しておくのは百害あって一利なし。# env - PATH=/usr/local/bin:/usr/bin:/bin /etc/init.d/hoge start_ 場合によってはセキュリティホールにもなりうることなので、本来はそれぞれのデーモンが自力で unsetenv(3) するべきだと思うんだけど、そういうものは残念ながら多くない(ないわけではない)。やってくれないのならば、自分でやるしかない。env - でラップして起動する習慣をつけましょう。
_ そんなつもりはまったくなかったのになぜか m4 が 入っちゃったので、ちまちまと解いてたり。わしだってめったに使わないツールだからわかんないんだけどなぁ。
_ つーか、m4 ってどうやって標準入力を読むの? man を見ても info を見ても書いてないから、今のところしかたなく include(/dev/stdin) (ゴルフ的には /dev/stdin より /dev/fd/0)として無理矢理読んでるけど、場合によってはうまくかないことがあるんだよね。たとえば、 素数生成の問題を解かせるとき、
このマクロをdefine(`r',`ifelse($2,1,1,eval($1%$2),0,,`r($1,decr($2))')')define(`p',`ifelse($2,0,,`ifelse(r($1,decr($1)),,`p(incr($1),$2)',`$1 p(incr($1),decr($2))')')')p(2,include(/dev/fd/0))として実行するとちゃんと動くんだけど、% echo -n 10 | m4 prime.m4は GNU m4 では無限ループになっちゃう。include(/dev/fd/0) のところを esyscmd(cat)(ゴルフ的には cat より dd)にかえればどっちでも大丈夫なんだけど、どう違うのかよくわからん。どっちにしろ外部コマンドを呼ぶのは NG だから esyscmd() は使えんし。% echo 10 | m4 prime.m4_ ちなみに、上のマクロは正しい解答を出すはずなんだけど、100個生成でタイムアウトするので受け付けてもらえません:-)。まあ、たしかに効率的な解法ではない。
_ 引き続き苦戦中。
_ たとえば この問題。手元の GNU m4 では以下で動いた。
が、アップロードすると、入力例の1と3でエラーになる。同じ GNU m4 なのになんでー、と2日悩んだあげく、エラーになる例では入力の最下行に # が含まれていることに気がつく。うわ、m4 のコメント記号だ……(dnl もコメントだが、それとは意味が違う)。translit(translit(include(/dev/fd/0),A-Z,a-z),a-z+-,222333444555660707788899901*)_ つまり、
というマクロで標準入力から HOGE#FUGA<改行> という文字列が与えられたとき、m4 はtranslit(include(/dev/fd/0),A-Z,a-z)と解釈する。# はそれ以降改行までマクロ処理はされないけど出力はされるというビミョーな動作をするので、ちゃんと hoge#fuga<改行> と変換される。ところが、入力が HOGE#FUGA(改行なし)だと、translit(HOGE#FUGA ,A-Z,a-z)となる。# 以降はコメントなので、これはtranslit(HOGE#FUGA,A-Z,a-z)と同じ。カッコの対応が取れないのでこれはエラーになる。そんな罠ありかよっ。translit(HOGE_ というわけで、あなごるではどうやら入力の最後の行には改行が含まれないらしいことがわかった。それがわかれば対処はできる。changecom(%) でコメント記号を # から % に変えてやる。この問題には % が含まれないので、やっとエラーにならずに通った。12バイト増えたけど。
_ そういえば、あなごるは出力の末尾の改行を無視してなかったっけ。というわけで、
をtranslit(include(/dev/fd/0),A-Z,a-z)と書き換えてやる。改行なしの hoge#fuga が入力されても、translit(include(/dev/fd/0) ,A-Z,a-z)になるので、エラーにはならない。余計な改行が増えるけど。でも、これで正解と受けつけてくれたので、まあよし。1バイト増で済んだ。translit(hoge#fuga ,A-Z,a-z)改行2個を1個に置換する。簡単。でもコケる。hoge,fuga という入力が与えられると、patsubst(include(/dev/fd/0),` ',` ')になる。patsubst() に与えられる引数の数がおかしいというエラーになってコケる。include() の結果を何とかしてクォートできれば回避できるんだけど、その方法が見つからない。たとえば `include(...)' としてやると、マクロではなく include(...) という単なる文字列になってそもそもインクルードされなくなる (*1)。どーすりゃいいの? だれか教えて。patsubst(hoge,fuga,` ',` ')_ もっとわからん問題。以上はすべて GNU m4 での現象だけど、FreeBSD の m4 ではそもそもぜんぶコケる。あなごるでは関係ないし、実装仕様の違いだ、と一言で片付けてしまえば話はラクなんだけど、今後もしこういうことをやりたいときがあったらどう対処したらいいの?
_ つーか、いまどき autoconf と sendmail.cf 以外で m4 を使うやつなんかいるのか。
(*1): 正確には、patsubst() の結果が inlcude(...) という文字列になる(今度はマクロとして解釈される)ので、patsubst の後で include が実行されて、置換されない元の入力がそのまま出力される。