open_port 日本語ドキュメント

# はじめに
open_portの全仕様が書かれた日本語のドキュメントが見つからなかったため、自分で書くことにした。

（勉強も兼ねて。）

以下のドキュメントを素にしている。

> 公式ドキュメント（もちろん英語）
> http://erlang.org/doc/man/erlang.html#open_port-2

# Spec
```erlang:spec.hrl
-spec open_port(PortName, PortSettings) -> port() when
      PortName :: {spawn, Command :: string() | binary()}
                | {spawn_driver, Command :: string() | binary()}
                | {spawn_executable, FileName :: file:name()}
                | {fd, In :: integer() >= 0, Out :: integer() >= 0},
      PortSettings :: [Opt],
      Opt :: {packet, N :: 1 | 2 | 4}
           | stream
           | {line, L :: integer() >= 0}
           | {cd, Dir :: string() | binary()}
           | {env, Env :: [{Name :: string(), Val :: string() | false}]}
           | {args, [string() | binary()]}
           | {arg0, string() | binary()}
           | exit_status
           | use_stdio
           | nouse_stdio
           | stderr_to_stdout
           | in
           | out
           | binary
           | eof
           | {parallelism, Boolean :: boolean()}
           | hide.
```

# 仕様

* 新しいErlangポートを開いてポート識別子を返す
    * ポートは外部Erlangプロセスとして扱える
* システムがUnicodeファイル名モードで実行されている場合、以下のものはすべてUnicodeファイル名変換の対象
    * 実行可能ファイル名
    * ```cd``` 、 ```env``` 、 ```args``` 、 ```arg0``` の引数
* 変換を避ける、または、UTF-8に強制するためには
    * 正しいエンコーディングバイナリとして実行ファイルと引数を与える
    * 詳細は、 [file](http://erlang.org/doc/man/file.html)モジュール 、 [```file:native_name_encoding/0```](http://erlang.org/doc/man/file.html#native_name_encoding-0) 、 [```stdlib``` ユーザーズガイド](http://erlang.org/doc/apps/stdlib/unicode_usage.html)を参照

## 注意

* ErlangのVMをUnicodeファイル名変換モードで起動した場合
    * 名前(Listの場合)の文字 > 255 のみ
* そうでない場合
    * 実行可能ファイル名はISO-Latin-1文字セットに制限される

## ```PortName```

### ```{spawn, Command}```

* **外部プログラムを起動**
    * ```Command```
        * 起動する外部プログラムの名前
        * 同名のErlangドライバが存在しない
            * Erlangのワークスペース外で ```Command``` を実行する
        * 同名のErlangドライバが存在する
            * そのドライバを起動
            * Erlangのワークスペース内で実行される<br/>
              = Erlangランタイムシステムにリンクされる
        * スペースで区切った最初のトークン<br/>
          = **実行可能ファイル（or ドライバ）の名前**
            * ファイルまたはディレクトリ名にスペースを持つプログラムの実行に適さない<br/>
              → 代わりに ```{spawn_executable, Command}``` を使う
* **Solaris** の場合
    * システムコールvforkが使われる
        * パフォーマンス上の理由でforkより優先される
        * 信頼性は低い
        * vforkを使うことに問題がある場合
            * 環境変数 ```ERL_NO_VFORK``` に任意の値をセット<br/>
              → forkを代わりに使うことができる
* 実行時、 ```PATH``` を使う
    * OSによってはプログラムを見つけるために環境メソッドを使う
    * 特定プラットフォーム上でシェルを起動することによって決まる

### ```{spawn_driver, Command}```

* ```{spawn, Command}``` のように動作
    * ```Command``` のスペース区切りトークンの最初<br/>
      → *ロードされたドライバの名前でないといけない*
        * その名前のドライバがロードされていない場合
            * エラー ```badarg``` が発生

### ```{spawn_executable, FileName}```

* ```{spawn, FileName}``` のように動作
    * 外部実行可能ファイルの実行しかできない
    * ```FileName```
        * 全体で実行可能ファイルの名前
            * スペースを含む
        * 引数を渡す場合
            * ```PortSettings``` の ```args``` と ```arg0``` を使う
* シェルは直接実行される
    * 通常プログラムを起動するために呼び出されていない
    * ```PATH``` 等は検索されない
        * 実行するプログラムの ```PATH``` を検索するには [```os:find_executable/1```](http://erlang.org/doc/man/os.html#find_executable-1) を使う
* シェルスクリプトまたは .batファイルが実行された場合だけ<br/>
  → 適切なコマンドインタプリタが暗黙的に呼び出される
    * しかし、まだコマンドの引数の拡張、暗黙的な ```PATH``` の検索はされない
* ```FileName``` が実行できない場合
    * POSIXのエラーコードとエラー例外が発生
        * エラーの理由はOS間で異なる可能性あり
        * 発生するエラー
            * ```enoent```
                * 見つからないプログラムが実行されようとしたとき
            * ```eaccess```
                * 与えられたファイルが実行可能でないとき

### ```{fd, In, Out}```

* Erlangプロセスはファイルディスクリプタにアクセスすることができる
    * 対象のファイルディスクリプタ
        * 現在開いている
        * Erlangによって使用されている
        * ```In```
            * 標準入力として使用することができる
        * ```Out```
            * 標準出力として使用することができる
    * Erlang OS(shell, user)の様々なサーバでのみ利用される<br/>
      → 仕様は非常に限られる

## ```PortSettings```

### ```{packet, N}```

* メッセージの先頭に長さが ```N``` バイト長で送られる
    * ```N```
        * 1, 2, 4のどれかのみ

### ```stream```

* 出力メッセージをパケット長なしで送信
* ユーザ定義のプロトコルを使用しなければならない
    * Erlangプロセス - 外部オブジェクト間

### ```{line, L}```

* メッセージが行ごとに配信される
    * それぞれの行は1つのメッセージで配信
        * 行: OS依存の改行文字で区切られている
* メッセージのデータ形式は ```{Flag, Line}```
    * ```Flag```
        * ```eol``` または ```noeol```
    * ```Line```
        * 配信された実際のデータ
            * 改行文字を持たない
* ```L```
    * 最大の行の長さ
        * バイト単位
    * これよりも長い行は、複数のメッセージで送られる
        * ```Flag``` に ```noeol``` が設定されている
        * 最後のメッセージは ```eol```
            * EOFが来たり、すぐに新しいシーケンスが続かなかったりした場合、 ```noeol```
* ```{packet, N}``` と ```{line, L}``` は相互排他

### ```{cd, Dir}```

* ```{spawn, Command}``` と ```{spawn_executable, FileName}``` にのみ有効
* 外部プログラムの作業ディレクトリが ```Dir``` になる
    * ```Dir``` は ```string()```

### ```{env, Env}```

* ```{spawn, Command}``` と ```{spawn_executable, FileName}``` にのみ有効
* 環境仕様に ```Env``` を追加
    * ```Env``` は ```{Name, Val}``` のリスト
        * ```Name``` は 環境変数名
            * ```string()```
        * ```Val``` は 生成されたポートプロセスで持つ値
            * ```string()```
            * 例外として ```false``` が使える
                * 環境変数を除去
                * ```os:getenv/1``` と同じ

### ```{args, [string() | binary()]}```

* ```{spawn_executable, FileName}``` に有効
* 実行可能ファイルに引数を指定
    * 各引数は別々の文字列として与えられる
    * Unix上
        * 引数ベクトルにおける一つの要素
    * 他のプラットフォーム
        * 似たような挙動
* 引数はシェルによって展開されない
    * 特に注意すべきことは **ワイルドカードが展開されないこと**
        * ワイルドカードを展開するには [```filelib:wildcard/1```](http://erlang.org/doc/man/filelib.html#wildcard-1) を使う
        * これはUnixシェルスクリプトでも同じ
            * ワイルドカードが展開されずに実行される
        * Windows®では、ワイルドカード展開はプログラム自身で行っているので問題にならない
* 実際に実行するファイル名( ```argv[0]``` )はこのリストで指定しない
    * 適切な実行可能ファイル名が ```argv[0]``` として使われる
    * 何らかの理由で引数ベクトルにプログラム名を明示的に設定したい場合は ```argv[0]``` を使う

### ```{arg0, string() | binary()}```

* ```{spawn_executable, FileName}``` でのみ有効
    * 実行可能ファイルを実行するときにプログラム名引数を明示的に指定
    * 一部のOSの特定の状況下では望ましい時がある
        * プログラムの返答の仕方はかなりシステムに依存
        * 具体的な影響は保証されない

### ```exit_status```

* ```{spawn, Command}``` と ```{spawn_executable, FileName}``` で有効
    * ```Command``` は *外部プログラム* のみ
* 外部プロセス終了時、 ```{Port, {exit_status, Status}}``` が送られる
    * ```Status``` は外部プロセスの終了ステータス
        * Unix上で以上終了した時はシェルと同じ規則
            * 128+signal
* ```eof``` オプションが指定されている場合
    * ```eof``` メッセージと ```exit_status``` メッセージの順序は決まっていない
* ポートプログラムが終了せず、標準出力を閉じた場合は、 ```exit_status``` は機能しない

### ```use_stdio```

* ```{spawn, Command}``` と ```{spawn_executable, FileName}``` で有効
    * (UNIX)プロセスの標準出入出力が使用可能になる
        * ファイルディスクリプタ0、1

### ```nouse_stdio```

* ```use_stdio``` の逆
    * Erlangとの通信にファイルディスクリプタ3, 4を使用

### ```stderr_to_stdout```

* 外部プログラムへのポートに影響
* 標準エラー出力を標準出力に出す
* ```stderr_to_stdout``` と ```nouse_stdio``` は相互排他

### ```overlapped_io```

* 外部プログラムへのポートに影響
    * **Windows®** でのみ
* ポートプログラムが標準入力と標準出力ハンドル上でオーバーラップされたI/Oを扱う
    * 標準ハンドルが ```FILE_FLAG_OVERLAPPED``` と一緒に開かれる
* 単純なポートプログラム用で経験の豊富なWindowsプログラマのためのオプションではない
* *他のプラットフォームでは無視される*

### ```in```

* 入力のためだけにポートが使える

### ```out```

* 出力のためだけにポートが使える

### ```binary```

* ポートからのすべてのI/Oが ```binary()``` になる

### ```eof```

* portを開いたまま、 ```{Port, eof}``` メッセージをポートを保持しているプロセスに送信する

### ```hide```

* ポートプログラムを起動するときに、新しいコンソールウィンドウを作らない
    * Windows上で実行している場合
    * 他のプラットフォームには影響を与えない

### ```{parallelism, Boolean}```

* ポートの並列処理のためにスケジューラヒントを設定
    * ```true```
        * システム内の並列性を向上させようとする
        * VMはシステム内で並列処理を改善するためにポートタスクをスケジューリングする
    * ```false```
        * VMは並列性を犠牲にしてレイテンシを改善
        * すぐにポートのタスクを実行しようとする
* デフォルトは[erl](http://erlang.org/doc/man/erl.html)起動時にコマンドライン引数[+spp](http://erlang.org/doc/man/erl.html#+spp)で設定
* 対象
    * ポートのすべての型のためのstream
    * 生成されたポートのための ```use_stdio```

## エラー

* ポートがオープンできない場合
    * ```badarg```
    * ```system_limit
* Posixのエラーコード
    * もっとも詳細にエラーを説明
* 対応するPosixコードがない場合は ```einval```

### ```badarg```

* ```open_port``` の引数の問題

### ```system_limit```

* Erlangエミュレータで利用できるポートがない

### ```enomem```

* 十分なメモリがない
    * ポートを生成するための

### ```eagain```

* 利用可能なOSのプロセスがない

### ```enametoolong```

* 与えられた外部コマンドが長過ぎる

### ```emfile```

* 利用可能なファイルディスクリプタがない
    * Erlangエミュレータが走るOSプロセスのための

### ```enfile```

* ファイルテーブルがいっぱい
    * 全OS

### ```eacces```

* ```{spawn_executable, Command}``` で与えられた ```Command``` が実行可能ファイルでない

### ```enoent```

* ```{spawn_executable, FileName}``` で与えられた ```FileName``` が存在しない

### ```{'EXIT', Port, PosixCode}```

* ポート使用時のエラー
* ポートを使用しているプロセスに伝えられる
* 対象
    * ```{spawn, Name}``` 、```{spawn_driver, Name}``` または ```{spawn_executable, Name}```
* ```PosixCode``` の取り得る値については file (?) を参照
* 同時に開くことができるポートの最大数
    * [erl](http://erlang.org/doc/man/erl.html)に[+Q](http://erlang.org/doc/man/erl.html#max_ports)コマンドラインフラグを渡すことによって設定

# 外部サイト

> 公式ドキュメント（もちろん英語）
> http://erlang.org/doc/man/erlang.html#open_port-2

> いろはメモ - Erlang 編 - 「は」 : ポートによる外部接続 - lnzntの Erlang 日記 - はてなグループ: erlang
> http://erlang.g.hatena.ne.jp/lnznt/20110814/1313283961


# 書籍

- **「プログラミングErlang」** には
    - 基本的な使い方の記述あり
    - 良く使われる ```PortName``` と ```Opt``` だけ紹介
- **「すごいErlangゆかいに学ぼう」** は…