C99未定義の動作を試すとこうなる

このコードはC99としてコンパイルすることが出来る。(注: 最近のgccでは問題の式について警告を発します)

#include <stdio.h>

int print(int num, int args[])
{
        int i;
        for(i = 0; i < num; ++i) {
                printf("%d", args[i]);
        }
        return 1;
}

int main()
{
        int a = 2;
        return print(1,(int[]){print(1, (int[]){print(2, (int[]){++a, print(1, (int[]){a + print(1, (int[]){print(2, (int[]){++a, ++a})})})})})}) && print(1, (int[]){a});
}

しかし、C99規格では

  • 初期化子の並びの副作用実行順序は未規定

とされているため、このコードの実行結果は未定義とされる。関数や完結式(ある式の部分式でない式)が副作用完了点とされているので、returnの式中にある前置インクリメントの式たちは未定義の動作とはならない、はず。
未定義の動作を2つの異なるコンパイラで体験してみよう。
一つ目はgcc、言わずと知れたメジャーなCコンパイラの一つだ。大体のLinux環境や*BSD環境にインストールされている。
コンパイル&実行すると、

$ gcc --version
gcc (GCC) 4.2.1 20070719
Copyright (C) 2007 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ gcc -std=c99 test.c && ./a.out
551651115

こうなる。
二つ目はPCC、30年ほどの歴史を持つ老舗のCコンパイラーだ。30年ぶりにC99対応をひっさげて1.0.0がでたとか、OpenBSDがソースツリーに取り込んだという話を聞いたことがあるけど、その後の報道がない。
コンパイル&実行すると、

$ pcc --version
pcc 1.1.0.DEVEL 20111207 for i386-unknown-openbsd5.0

$ pcc -std=c99 test.c && ./a.out
341551115

gccと1、2、4番目の出力が異なる。
それぞれコンパイラが生成した実行順序について考察してみるとおもしろいだろう。

元ネタ(written by [twitter
@fadis_]):http://ideone.com/B3dob

IIS上のPowershell CGI ScriptでPOSTリクエストを処理する

IISではCGIモジュールを利用することで、スタンドアロン形式のスクリプト実行プログラムを通してCGIスクリプトを実行することが出来る。PowerShellも当然ながら例外ではない。検索エンジンで適当なワードを検索すれば、いくつもの導入事例を見つけられるだろう。
しかし、PowershellでのPOSTメソッドを使った導入事例については、探してみても見当たらなかった。試行例はあるようだけど、(CGIモジュールで設定した)タイムアウトまで戻ってこないという結論だった。
CGIにおけるPOSTリクエストの処理は、環境変数と標準出入力を用いた、ごく単純なデータのやりとりなので、Powershellだけ無理と言うことは考えにくい。
それでは、ちょっと頭をひねって実現方法を考えてみよう。
POSTメソッドによるリクエストでは、

  1. エンティティボディはバイナリデータとして読み取る
  2. エンティティボディのサイズは環境変数のCONTENT_LENGTHで受ける

が一般的な知識になっている。
これをPowershellの世界に置き換えると、読み込みに[Console]::Inや、[Console]の読み込みメソッドを直接使わず、BinaryReaderないしBinaryStreamを通して CONTENT_LENGTH バイトだけを読め、ということになる。
これをコードに落としてみよう。

$bodyReader = New-Object System.IO.BinaryReader -ArgumentList @([Console]::OpenStandardInput())
$bodyBytes = $entityBodyReader.ReadBytes($ENV:CONTENT_LENGTH)

実際に使うには、CONTENT_LENGTHに対する範囲チェックや、例外捕捉が必要になるけど、ここでは割愛する。
実はここにPowershellの落とし穴が1つあって、-InputFormatコマンドラインオプションで、バイナリデータ相当の受付入力書式に切り替えなければならない。さもなくばハングアップする。

PowerShell[.exe]
(snip)
[-InputFormat {Text | XML}]

PowerShell.exe Console Help | Microsoft Docs

ヘルプではText(テキスト)かXML(XML)しかないように見えるが、None(?)という隠しオプション(?)を指定することが出来て、これがバイナリデータ相当になるらしい。IISのハンドラ設定で、Powershellハンドラにスクリプトファイルパスを与える-Fileしか指定していない場合は、この-InputFormatオプションも指定する必要がある。
標準出力にレスポンスを出力する部分については、UTF-8である限り Echo で問題ない。
問題は出力しきった後、スクリプトを終了するときだ。不確かな情報だけど、IISはPOSTリクエストの時、Powershellを自動的に終了しないという。

POWERSHELL.EXE will hang because on a remote script from IIS because it does not close off the POWERSHELL.EXE process automatically... all you have to do is add NUL to the end of the command.

powershell script does not complete when launched from IIS : The Official Microsoft IIS Forums

コマンドの最後にNULを付加すればいいとあるけど、いまいち解せない。
試しに標準出力にNULを付加し、Exitでみずから終了すれば動くことが分かった。Out.Writeはいらないかも知れない。

[System.Console]::Out.Write([char]0)
[System.Environment]::Exit(0)

これらをまとめて、

  • inputフィールド"name","id"のShift_JIS HTMLフォームからPOSTで受け取る
  • TABLEでフィールド名、フィールド値を返す

という例で、一つのコードに表現してみよう。

#requires -version 2.0
Add-Type -AssemblyName System.Web
Add-Type -AssemblyName System.Web.Extensions

if ($Env:CONTENT_TYPE -ne "application/x-www-form-urlencoded") {
        throw New-Object System.IO.InvalidDataException -ArgumentList@("Unexpected content type")
        [System.Environment]::Exit(-1)
}

$entityBodyReader = New-Object System.IO.BinaryReader -ArgumentList @([Console]::OpenStandardInput())
$entityBody = [System.Text.Encoding]::ASCII.GetString($entityBodyReader.ReadBytes($ENV:CONTENT_LENGTH))
$queryValues = [System.Web.HttpUtility]::ParseQueryString($entityBody, [System.Text.Encoding]::GetEncoding(932))

Echo "Content-Type: text/html"
Echo ""
Echo "<HTML><BODY><TABLE>"
Echo "<TR><TD>id</TD><TD>$($queryValues.Get(`"id`"))</TD></TR>"
Echo "<TR><TD>name</TD><TD>$($queryValues.Get(`"name`"))</TD></TR>"
Echo "</TABLE></BODY></HTML>"

[System.Console]::Out.Write([char]0)
[System.Environment]::Exit(0)

こんなことは、目立つところに誰かまとめてくれたらいいのに!
え、IISで、PowershellCGIスクリプトを組んで、POSTリクエストを処理する方がおかしい?それはごもっとも。

Thunderbird8の迷惑メールフィルタがバグっている件について

普段メールを送受信するのにThunderbirdを使っているのだけど、最近のバージョンになって、迷惑メールフィルタの調子がおかしい。手動で迷惑メールフィルタを実行すると、大体5,60件ぐらいマークを付けて止まってしまう。再度実行すると、マークが付いていないメールについてチェックしてくれるのだけど、これも同じぐらいの件数で止まってしまう。
迷惑メールフィルタ実行を連打すれば、全てのメールをチェックできるとは言え、これはあまりにも不便過ぎる。
当然ながら、一般ユーザにとっては不可解極まりない不具合として映るわけで、Bugzilla に速攻でポストされていた。

Actual results:

the first approximately 20 messages were marked junk (correctly) in approximately one second. It then stopped checking and the remaining 980 messages were not checked to be junk or not.
gMessengerBundle is not defined
chrome://messenger/content/junkCommands.js Line: 276

697740 - "Run Junk Mail Controls on Folder" quits early with error gMessengerBundle is not defined chrome://messenger/content/junkCommands.js Line: 276

報告内容から、junkCommands.jsで定義されていない変数gMessengerBundleを参照して転けているらしいことが分かる。ソースコードを当たってみると確かにそうだ。

         window.MsgStatusFeedback.showStatusString(
            gMessengerBundle.getFormattedString("junkAnalysisPercentComplete",

で、gMessengerBundleとは何なのか……上記のコード片からは、文字列資源を管理しているオブジェクトである以上のことは読み取れない。どうしてそんな重要なものが消えているの?
とりあえず検索してみると、今度は次のBugzillaに行き着いた。

This patch removes gMessengerBundle and gBrandBundle. It also includes a bit of code to clean up how we open windows, but that part should be very straightforward.

458548 - Get rid of string-bundle globals

んん、このパッチはgMessengerBundleを削除します……?パッチをのぞいてみると、迷惑メールフィルタでgMessengerBundleを使っている部分を消去する差分記述がない。
comm-releaseのTHUNDERBIRD_8_0_RELEASEタグが付いたツリーと、comm-centralのDEFAULTツリーを確認してみると、確かにgMessengerBundleの宣言が消えていて、迷惑メールフィルタのgMessengerBundleの参照が消えていない。つまり、このパッチで見逃しを食らっていたと言うことか。
連打するのも腹立たしいので、パッチを書いて、omni.jarの中身に適用してきた。ソースコードツリー(base=THUNDERBIRD_8_0_RELEASE)に対するパッチは以下の通り。

diff -ur a/mailnews/base/content/junkCommands.js b/mailnews/base/content/junkCommands.js
--- a/mailnews/base/content/junkCommands.js	2011-11-05 17:49:32.000000000 +0900
+++ b/mailnews/base/content/junkCommands.js	2011-12-06 10:44:38.000000000 +0900
@@ -261,6 +261,7 @@
     else if (aClassification == nsIJunkMailPlugin.GOOD)
       this.mGoodMsgHdrs.appendElement(msgHdr, false);
 
+    let bundle = document.getElementById("bundle_messenger");
     var nextMsgURI = this.mMessageQueue.shift();
     if (nextMsgURI)
     {
@@ -273,7 +274,7 @@
           percentDone = Math.round(this.mProcessedMessages * 100 / this.mTotalMessages);
         var percentStr = percentDone + "%";
         window.MsgStatusFeedback.showStatusString(
-            gMessengerBundle.getFormattedString("junkAnalysisPercentComplete",
+            bundle.getFormattedString("junkAnalysisPercentComplete",
                                                 [percentStr]));
       }
 
@@ -284,7 +285,7 @@
     else
     {
       window.MsgStatusFeedback.showStatusString(
-          gMessengerBundle.getString("processingJunkMessages"));
+          bundle.getString("processingJunkMessages"));
       performActionsOnJunkMsgs(this.mFolder, this.mJunkMsgHdrs, this.mGoodMsgHdrs);
       window.MsgStatusFeedback.showStatusString("");
     }

DEFAULTまで見逃されているってどういうことよ……。

追記(2012/01/06)

bugfixがcheck inされました、がTB9に間に合わず、TB10に投入予定だそうです。
死ぬがよい

Powershell 2.0でチャーチ数の夢を見た

(***) $one=&{params($f)&{params($X)f x}};とか受け付けてくれない残念なps1

と言われて、最初は何のことか分からなかったけど、なるほどチャーチ数であることに気付いた。
折角なので受けて立とう、その挑戦を。

全ては1から

チャーチ数の1は以下のように表される。

ONE := λf.λx. f x

愚直にλ項をスクリプトブロック相当と考えて組むと

$one={param($f){param($x)&$f($x)}}

となる。
しかし、これを使って評価しようとすると、思わぬエラーと遭遇することになる。

> &$(&$one({param($x)$x+1}))(0)
パイプライン要素内で '&' の後にある式が無効なオブジェクトを作成しました。コマンド名、スクリプト ブロック、または CommandInfoオブジェクトになる必要があります。
発生場所 行:1 文字:27
+ $one={param($f){param($x)& <<<< $f($x)}};
    + CategoryInfo          : InvalidOperation: (:) []、RuntimeException
    + FullyQualifiedErrorId : BadExpression

$fが無効なオブジェクト?どういうことだろうか。
実はスクリプトブロックは環境を保持しない、つまり、クロージャではない。どなたかの研究によると、変数はスタックに確保されるらしく、変数の定義されたスクリプトブロックから出ると消されてしまう。そのため、入れ子になっているスクリプトブロックで$fを参照しようとすると$Nullとなり、このエラーを見るハメになったわけだ。

そこで活躍するのが、Powershell 2.0から登場したスクリプトブロックをクロージャに変換するメソッド、ScriptBlock::GetNewClosureだ。クロージャにしてしまえばその時点での環境が保存されるので、環境中にある変数を参照出来るようになる。

> $ONE={param($f){param($x)&$f($x)}.GetNewClosure()}
> &$(&$one({param($x)$x+1}))(0)
1

すごくまどろっこしい書き方になっているけど、これは致し方ない。
これで、チャーチ数の1をPowershellで記述することが叶った。

気づくわけがない落とし穴

勢いに乗って次のチャーチ数の元を得る関数SUCCを実現してみよう。
SUCCの定義は次の通りだ。

SUCC := λn.λf.λx. f (n f x)

単位元の反省を元にきっちりクロージャで書こう。

$succ={param($n){param($f){param($x)&$f(&$(&$n($f))($x))}.GetNewClosure()}.GetNewClosure()}

これでTWOを求めてみると、

> $succ={param($n){param($f){param($x)&$f(&$(&$n($f))($x))}.GetNewClosure()}.GetNewClosure()}
> &$(&$(&$succ($one))({param($x)$x+1}))(0)
パイプライン要素内で '&' の後にある式が無効なオブジェクトを作成しました。コマンド名、スクリプト ブロック、または CommandInfo オブジェクトになる必要があります。

発生場所 行:1 文字:45
+ $succ={param($n){param($f){param($x)&$f(&$(& <<<< $n($f))($x))}.GetNewClosure()}.GetNewClosure()}
    + CategoryInfo          : InvalidOperation: (:) []、RuntimeException
    + FullyQualifiedErrorId : BadExpression

パイプライン要素内で '&' の後にある式が無効なオブジェクトを作成しました。コマンド名、スクリプト ブロック、または CommandInfo オブジェクトになる必要があります。
発生場所 行:1 文字:42
+ $succ={param($n){param($f){param($x)&$f(& <<<< $(&$n($f))($x))}.GetNewClosure()}.GetNewClosure()}
    + CategoryInfo          : InvalidOperation: (:) []、RuntimeException
    + FullyQualifiedErrorId : BadExpression

???どうして?どういうことだろう?
エラーメッセージは、$nが関数呼び出し出来ない値を持っていると言っている。考えられる値はただ1つ、$Nullだ。クロージャで$nは保存されているはずだ。$Nullになってしまうはずがない。……ない?
そう、ここにGetNewClosureメソッドの落とし穴があった。
GetNewClosureはブロックで参照されていない変数を落としてしまうらしく、中間のスクリプトブロックで参照されていないがために、一番深いスクリプトブロックで$nが参照出来なくなっていたというのだ。
解決策を考えてみたところ、1つ思い浮かんだ。入れ子スクリプトブロックが参照している変数を中間のスクリプトブロックが影響の無い程度に参照してやるのだ。

>$succ={param($n){param($f)$n=$n;{param($x)&$f(&$(&$n($f))($x))}.GetNewClosure()}.GetNewClosure()}
>&$(&$(&$succ($one))({param($x)$x+1}))(0)
2

この解決策を以て、無事にTWOを求めることが出来た。内部仕様が分からないので、何とも言えないが、GetNewClosureによるクロージャは完全な環境を保存しているわけではなさそうだ。
まさか、まさかこんな課題でこんな現象に出会うとはね?

.NET Framework < 4.0でEnumerateFiles/EnumerateDirectories

最近になって、プログラミングの世界に舞い戻ってきた。別に必要でもなんでも無いから遠ざかっていて、今も似たような感じだけど、なぜか唐突に始めた感じだ。
さて、昨日今日でやっつけたC#プログラムを作る際、どうしても数万ファイルを探索することになる処理が出てきた。
そういった処理は、例によってDirectory.GetFilesをAllDirectoriesオプションを付けて呼び出し、繰り返しで検索するのがテンプレだ。しかし、GetFilesは配列を返すメソッドなので、ファイル数が多くなってくると、やたらと時間を食うしリソースも食う。
これのイテレータを返すバージョンは無いの?と疑問に思って探してみると、.NET Framework 4.0からの実装ということが分かった。
4.0から?2.0からの間違いじゃないの?どうしてこんな機能を実装してなかったんだ……
嘆いていたって仕方ないので自力救済を図るしかない。従来のWin32アプリケーションは普通、ファイルの列挙にFind***系APIを用いる。Directory.GetFilesなどはこれのフロントエンドだろう。それに倣って、P/Invokeでそれらを呼び出し、yieldをぶん回す関数を考え付いた。
どこかで誰か思いついてそうなネタだろうけどね。

using System;
using System.IO;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;

namespace ExtIO
{
    public static class DirectoryEx
    {
        internal static readonly IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1);

        internal class SafeFindHandle : SafeHandle
        {
            public SafeFindHandle()
                : base(INVALID_HANDLE_VALUE, true)
            {
            }

            internal SafeFindHandle(IntPtr existingHandle, bool ownsHandle)
                : base(existingHandle, ownsHandle)
            {
            }

            public override bool IsInvalid
            {
                get { return IsClosed || handle == INVALID_HANDLE_VALUE; }
            }

            protected override bool ReleaseHandle()
            {
                if (!IsInvalid)
                {
                    FindClose(handle);
                    return true;
                }
                return false;
            }
        }

        // From P/Invoke.net
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        internal struct WIN32_FIND_DATA
        {
            public FileAttributes dwFileAttributes;
            public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
            public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
            public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
            public int nFileSizeHigh;
            public int nFileSizeLow;
            public uint dwReserved0;
            public uint dwReserved1;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
            public string cFileName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
            public string cAlternateFileName;
        }

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        internal static extern SafeFindHandle FindFirstFileW(string lpFileName, out WIN32_FIND_DATA lpFindFileData);


        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        internal static extern bool FindNextFile(SafeFindHandle hFindFile, out WIN32_FIND_DATA
           lpFindFileData);

        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern bool FindClose(IntPtr hFindFile);

        public static IEnumerable<DirectoryInfo> EnumerateDirectories(string searchPath, string searchPattern, SearchOption searchOption)
        {
            WIN32_FIND_DATA findData;
            Regex compiledPattern = new Regex(searchPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);

            using (SafeFindHandle findHandle = FindFirstFileW(Path.Combine(searchPath, "*"), out findData))
            {
                if (!findHandle.IsInvalid) {
                    do
                    {                       
                        if ((findData.dwFileAttributes & FileAttributes.Directory) != 0)
                        {
                            if (findData.cFileName != "." && findData.cFileName != ".." && compiledPattern.IsMatch(findData.cFileName))
                            {
                                string foundPath = Path.Combine(searchPath, findData.cFileName);
                                yield return new DirectoryInfo(foundPath);
                                if (searchOption == SearchOption.AllDirectories)
                                {
                                    foreach (DirectoryInfo subItem in EnumerateDirectories(foundPath, searchPattern, searchOption))
                                    {
                                        yield return subItem;
                                    }
                                }
                            }
                        }
                    } while (FindNextFile(findHandle, out findData));
                }
            }

            yield break;
        }

        public static IEnumerable<FileInfo> EnumerateFiles(string searchPath, string searchPattern, SearchOption searchOption)
        {
            WIN32_FIND_DATA findData;
            Regex compiledPattern = new Regex(searchPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);

            foreach (DirectoryInfo dirInfo in EnumerateDirectories(searchPath, ".*", searchOption))
            {
                using (SafeFindHandle findHandle = FindFirstFileW(Path.Combine(dirInfo.FullName, "*"), out findData))
                {
                    if (!findHandle.IsInvalid)
                    {
                        do
                        {  
                            if ((findData.dwFileAttributes & FileAttributes.Directory) == 0)
                            {
                                if (compiledPattern.IsMatch(findData.cFileName))
                                {
                                    string foundPath = Path.Combine(dirInfo.FullName, findData.cFileName);
                                    yield return new FileInfo(foundPath);
                                }
                            }
                        } while (FindNextFile(findHandle, out findData));
                    }
                }
            }
            yield break;
        }
    }
}

iphone-dev/trunk is so old

なんとかして、クロスGCCのビルド開始までこぎ着けたが、どうやら飛んでもない罠を踏んだようだ。
dylibのcmd7が認識できないという問題だが、これの原因が、どうもodcctoolsか、SDKのバージョンが古すぎるかららしい。
またodcctoolsのビルドからやり直さなければ……

odcctools on OpenBSD 4.6

LLVMは無事通ったが、第一の問題はこれだ。
Linuxコンパイルできるようにと、中にBSD系のヘッダを抱えているので、タチが悪い。

まずは、標準ではコンパイルに必要なautomake, autoconf がないので、pkg_addしておく。automakeを突っ込めば勝手に対応するautoconfが入るので、autoconfには触れなくていい。触れない。
パッケージを揃えたら、iphone-devを落としに行く。

$ sudo -s
# cd /usr/local/src
# svn checkout http://iphone-dev.googlecode.com/svn/trunk/ iphone-dev

落とせたら、ビルド用のディレクトリに潜って、configureする。

# mkdir -p build/odcctools
# cd build/odcctools
# ../../odcctools/configure --target=arm-apple-darwin --disable-ld64

ここまでは、LLVM同様マニュアル通りだが、ここからがダークネスな耐久ゲームが始まる。
とりあえず、主要な小細工をあげると、

_types.h

システムヘッダとダダ被り。Machだから仕方ない。
〜_t系型の多重定義を回避するために#ifマジックをいじる必要がある。
また、OpenBSD固有のヘッダでこけるので、Makefileで-D_LITTLE_ENDIANにする。

appleapiopt.h

Appleからappleapiopt.hを貰ってきてinclude/sys/に入れる。
システムには入れたらだめ。絶対。

ar/...

ENOSUP未対応。ar/Makefileで-DENOSUP=48 にする。どうせ使われないけど。

dylib_root.c

errnoがない。じゃなくて、です。

ld/passX.c

vm_msync使用回避のために、-D_POSIX_C_SOURCEをつける。Machじゃないのに、なんで使う分岐に行くんだコイツ。
さらに、L_SETなどという骨董品を使っていて困るので、vi等でSEEK_SETに置換する。

ARM

アサーションで死ぬ。MLの報告に因れば、OpenBSD mallocが割り付けランダムな所為じゃないのかと言われているけど、ランダムにされてラリってるのはいかがな物か。
半々の確率で成功するらしいので、ディレクトリを下って、ひたすらgmakeすべし。

あと細かな修正がいくつかあるけど、何とかすればビルド終了まで行ける。
今日はここまで。ダークネスな吸い出しだとか、SDKだとか、本命のLLVM-gcc for iPhoneはまた今度にする。