yunomuのブログ

酒とゲームと上から目線

Haskellで実行ファイル名をパス付きで取得する

CGIを書くときとか、実行ファイルが置いてあるパスが欲しい事がある。
設定ファイルやデータの読み書きをするために。

一応System.EnvironmentのgetProgNameで実行ファイルの名前は取れるんだけど、これにはパス名が付いてない。
getArgsにも入ってないし。
System.DirectoryにgetCurrentDirectoryがあるけど、カレントディレクトリじゃないんだよなぁ。特にCGI実行の時は。
どうすんだ。

と思ってググっていたら、Haskell-cafeでも同じ話題で延々と盛り上がってた。
http://www.haskell.org/pipermail/haskell-cafe/2011-December/097217.html

結論としては、そういう機能は無い。無いものは無いので、パス付きのファイル名を取得するためだけに作られたこのsystem-argv0パッケージを入れろってことらしいです。
http://hackage.haskell.org/package/system-argv0-0.1

argv0.hs

import System.Argv0

main :: IO ()
main = getArgv0 >>= print

これを実行する。

% ghc argv0.hs
% ./argv0
FilePath "./argv0"
% ../test/argv0
FilePath "../test/argv0"

おめでとうございます。

これくらいSystem.Environmentに入れてくださいよーって感じですよね。
ただ、ソースは見なかったことにしたい感じでした。Environmentだし仕方ないか。

おまけ

逆にWebサーバ側をいじる手もある。いや、無いけど。

CGI実行時にWebサーバのカレントディレクトリを変更するという手が無くはない。
単にMightyのソースいじってみましたという話です。

mighttpd2/FileCGIApp.hs

15 fileCgiApp :: ClassicAppSpec -> FileAppSpec -> CgiAppSpec -> RevProxyAppSpec -> RouteDB -> Application
(略)
30     Found (RouteCGI   src dst) ->
31         cgiApp cspec cgispec (CgiRoute src dst) req

このへんを

12 import System.Directory
(略)
16 fileCgiApp :: ClassicAppSpec -> FileAppSpec -> CgiAppSpec -> RevProxyAppSpec -> RouteDB -> Application
(略)
31     Found (RouteCGI   src dst) -> do
32         liftIO $ setCurrentDirectory $ pathString dst
33         cgiApp cspec cgispec (CgiRoute src dst) req

こんな感じに書き換えると、CGI実行時のカレントディレクトリがrouteで指定したディレクトリになります。これはこれで自然な動きな気がしますけどね。
実はパス付きのプログラム名を取得するよりこっちの方が簡単だったのでしばらくこれで動かしてました。