# 機能設計書 72-TTY初期化

## 概要

本ドキュメントは、libuv が提供する TTY ハンドル初期化機能 `uv_tty_init()` の設計仕様を記述する。この関数は、端末（TTY）デバイスに対するストリーム I/O ハンドルを初期化し、コンソールアプリケーションにおける対話的な入出力を可能にする。

### 本機能の処理概要

`uv_tty_init()` は、指定されたファイルディスクリプタを TTY ハンドルとして初期化する。内部ではファイルディスクリプタの種類判定、TTY スレーブデバイスの再オープン（他プロセスへの影響回避）、ノンブロッキングモード設定など、複数の初期化処理を実行する。

**業務上の目的・背景**：コンソールアプリケーション（CLI ツール、REPL、エディタなど）では、標準入出力を TTY として扱い、端末の特殊機能（RAW モード入力、ウィンドウサイズ取得など）にアクセスする必要がある。libuv は uv_tty_t を uv_stream_t のサブクラスとして提供し、イベントループ上での非同期 TTY 操作を実現する。特に Node.js のようなランタイムにおいて、端末入出力のクロスプラットフォーム抽象化は不可欠な機能である。

**機能の利用シーン**：CLI アプリケーションの標準入出力ハンドル初期化、REPL（対話型シェル）の入力処理、プログレスバーやスピナー等の端末 UI 実装、テスト環境でのコンソール出力キャプチャなどで利用される。

**主要な処理内容**：
1. `uv_guess_handle()` によりファイルディスクリプタの種類を判定し、TTY として適切か検証する
2. ファイルディスクリプタのアクセスモード（O_RDONLY / O_WRONLY / O_RDWR）を `fcntl(F_GETFL)` で取得する
3. TTY スレーブデバイスの場合、`ttyname_r()` でデバイスパスを取得し再オープンする（他プロセスへの非ブロッキング設定の影響を回避）
4. `uv__stream_init()` によりストリームハンドルとして基本初期化を行う
5. ノンブロッキングモードの設定（ブロッキング書き込みフォールバックあり）
6. macOS では `uv__stream_try_select()` によるkqueue対応処理を実行する
7. アクセスモードに基づいて `UV_HANDLE_READABLE` / `UV_HANDLE_WRITABLE` フラグを設定する

**関連システム・外部連携**：OS の TTY サブシステム、`termios` API、`ioctl` システムコール。

**権限による制御**：ファイルディスクリプタの所有権とアクセス権に依存する。通常、プロセスの標準入出力のディスクリプタ（0, 1, 2）が対象。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 20 | TTY | 主機能 | uv_tty_init()によるTTYハンドル初期化のAPI定義 |

## 機能種別

リソース初期化処理 - TTY ハンドルの生成と初期設定

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| loop | uv_loop_t* | Yes | イベントループ | 初期化済みのループであること |
| tty | uv_tty_t* | Yes | 初期化する TTY ハンドル | 有効なメモリが割り当てられていること |
| fd | uv_file (int) | Yes | TTY に関連付けるファイルディスクリプタ | UV_FILE / UV_UNKNOWN_HANDLE でないこと |
| readable | int | Yes | 非推奨パラメータ（無視される） | 任意の値（内部で使用されない） |

### 入力データソース

呼び出し元から直接パラメータとして渡される。通常は STDIN_FILENO (0)、STDOUT_FILENO (1)、STDERR_FILENO (2) などの標準入出力ファイルディスクリプタ。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | int | 成功時 0、失敗時 UV エラーコード |
| tty->mode | int | UV_TTY_MODE_NORMAL に初期化される |
| tty フラグ | unsigned int | UV_HANDLE_READABLE / UV_HANDLE_WRITABLE が設定される |

### 出力先

uv_tty_t 構造体が初期化され、イベントループのハンドルキューに登録される。

## 処理フロー

### 処理シーケンス

```
1. uv_guess_handle(fd) でハンドルタイプを判定
   └─ UV_FILE または UV_UNKNOWN_HANDLE の場合は UV_EINVAL を返却
2. fcntl(fd, F_GETFL) でアクセスモードを取得
   └─ O_ACCMODE マスクで読み書きモードを抽出
3. TTY タイプの場合のスレーブデバイス再オープン処理
   a. uv__tty_is_slave(fd) でスレーブかどうかを判定
   b. ttyname_r() でデバイスパスを取得
   c. uv__open_cloexec() で再オープン
   d. uv__dup2_cloexec() で元の fd にコピー
   └─ 再オープンに失敗した場合は UV_HANDLE_BLOCKING_WRITES フラグを設定
4. uv__stream_init() でストリームとして初期化
5. ブロッキング書き込みモードでなければ uv__nonblock(fd, 1) を実行
6. [macOS] uv__stream_try_select() でkqueue対応
7. アクセスモードに応じて READABLE / WRITABLE フラグを設定
8. uv__stream_open() でストリームを開く
9. tty->mode = UV_TTY_MODE_NORMAL に設定
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[uv_guess_handle fd]
    B --> C{UV_FILE or UV_UNKNOWN?}
    C -->|Yes| D[return UV_EINVAL]
    C -->|No| E[fcntl F_GETFL でモード取得]
    E --> F{type == UV_TTY?}
    F -->|Yes| G{uv__tty_is_slave && ttyname_r成功?}
    G -->|Yes| H[uv__open_cloexec で再オープン]
    H --> I[uv__dup2_cloexec で元fdにコピー]
    G -->|No| J[UV_HANDLE_BLOCKING_WRITES設定]
    F -->|No| K[skip]
    I --> K
    J --> K
    K --> L[uv__stream_init]
    L --> M[uv__nonblock設定]
    M --> N[uv__stream_open]
    N --> O[mode = UV_TTY_MODE_NORMAL]
    O --> P[return 0]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-72-01 | ハンドルタイプ検証 | UV_FILE と UV_UNKNOWN_HANDLE は TTY として初期化不可 | 常時 |
| BR-72-02 | スレーブデバイス再オープン | TTY スレーブデバイスは再オープンして他プロセスへの影響を回避する | type == UV_TTY かつスレーブデバイスの場合 |
| BR-72-03 | ブロッキング書き込みフォールバック | 再オープンに失敗した書き込み可能な TTY はブロッキング書き込みモードになる | 再オープン失敗かつ mode != O_RDONLY |
| BR-72-04 | readable パラメータ非推奨 | 第4引数 readable は非推奨パラメータであり内部では使用されない | 常時 |
| BR-72-05 | 初期モード設定 | 初期化完了後の TTY モードは UV_TTY_MODE_NORMAL | 常時 |

### 計算ロジック

特になし。

## データベース操作仕様

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| UV_EINVAL | パラメータエラー | fd が UV_FILE または UV_UNKNOWN_HANDLE と判定された | 有効な TTY ファイルディスクリプタを使用する |
| UV_EBADF | システムエラー | fcntl(F_GETFL) が失敗した | 有効なファイルディスクリプタを指定する |
| UV_EINVAL | システムエラー | uv__dup2_cloexec が失敗した（newfd == fd の場合を除く） | 再試行またはブロッキングモードで使用 |

### リトライ仕様

TTY スレーブデバイスの再オープンが失敗した場合、ブロッキング書き込みモードにフォールバックして処理を継続する。

## トランザクション仕様

該当なし。

## パフォーマンス要件

初期化処理はアプリケーション起動時に一度だけ実行される。TTY デバイスの再オープン処理を含むが、I/O 負荷は軽微。

## セキュリティ考慮事項

- 再オープン時に O_NOCTTY フラグを使用し、新しい制御端末の設定を防止する
- CLOEXEC フラグを設定し、子プロセスへのファイルディスクリプタ漏洩を防止する

## 備考

- `readable` パラメータは非推奨（deprecated）であり、内部で `(void)unused;` として無視される
- macOS では `uv__stream_try_select()` による追加の初期化が必要（kqueue が TTY を直接サポートしないため）
- IBM i PASE 環境では `isatty()` の挙動が異なるため、独自の `isreallyatty()` 関数で代替する
- uv_tty_t は uv_stream_t のサブクラスであるため、`uv_read_start()` / `uv_write()` 等のストリーム API が利用可能

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | uv.h | `include/uv.h` | **L806-810**: uv_tty_s 構造体定義。UV_HANDLE_FIELDS + UV_STREAM_FIELDS + UV_TTY_PRIVATE_FIELDS で構成 |
| 1-2 | uv.h | `include/uv.h` | **L812-824**: uv_tty_mode_t 列挙型。NORMAL / RAW / IO / RAW_VT の4モード |
| 1-3 | uv.h | `include/uv.h` | **L826-836**: uv_tty_vtermstate_t 列挙型。SUPPORTED / UNSUPPORTED |

**読解のコツ**: `uv_tty_t` は `uv_stream_t` のサブクラスであり、UV_HANDLE_FIELDS と UV_STREAM_FIELDS を包含する。C 言語での構造体継承パターン（最初のメンバーがベースクラスと互換）を理解すると読みやすい。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | tty.c (Unix) | `src/unix/tty.c` | **L136-242**: uv_tty_init() の全実装 |

**主要処理フロー**:
1. **150行目**: `uv_guess_handle(fd)` でファイルディスクリプタの種類を判定
2. **151-152行目**: UV_FILE / UV_UNKNOWN_HANDLE なら UV_EINVAL を返却
3. **158-160行目**: `fcntl(fd, F_GETFL)` で現在のフラグを保存（EINTR のリトライ付き）
4. **164行目**: O_ACCMODE マスクでアクセスモードを抽出
5. **176-207行目**: TTY スレーブデバイスの再オープン処理
6. **210行目**: `uv__stream_init()` でストリーム基本初期化
7. **216-217行目**: ノンブロッキングモード設定
8. **219-231行目**: macOS 固有の `uv__stream_try_select()` 処理
9. **233-236行目**: READABLE / WRITABLE フラグ設定
10. **238行目**: `uv__stream_open()` でストリームを開く
11. **239行目**: `tty->mode = UV_TTY_MODE_NORMAL` に設定

#### Step 3: ヘルパー関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | tty.c | `src/unix/tty.c` | **L82-134**: uv__tty_is_slave() - プラットフォーム別のスレーブデバイス判定 |
| 3-2 | tty.c | `src/unix/tty.c` | **L394-479**: uv_guess_handle() - ファイルディスクリプタからのハンドルタイプ推定 |
| 3-3 | tty.c | `src/unix/tty.c` | **L65-67**: グローバル変数 orig_termios_fd, orig_termios, termios_spinlock |

**読解のコツ**: `uv__tty_is_slave()` はプラットフォームごとに異なる実装を持つ（Linux: `TIOCGPTN` ioctl、macOS: `TIOCPTYGNAME` ioctl、NetBSD: デバイスメジャー番号比較、その他: `ptsname()`）。

### プログラム呼び出し階層図

```
uv_tty_init(loop, tty, fd, unused)
    |
    +-- uv_guess_handle(fd)
    |       +-- isatty(fd)
    |       +-- uv__fstat(fd, &s)
    |       +-- getsockname(fd, ...)
    |
    +-- fcntl(fd, F_GETFL)
    |
    +-- [type == UV_TTY]
    |   +-- uv__tty_is_slave(fd)
    |   |       +-- ioctl(fd, TIOCGPTN/TIOCPTYGNAME, ...) [Linux/macOS]
    |   |
    |   +-- ttyname_r(fd, path, sizeof(path))
    |   +-- uv__open_cloexec(path, mode | O_NOCTTY)
    |   +-- uv__dup2_cloexec(newfd, fd)
    |
    +-- uv__stream_init(loop, tty, UV_TTY)
    +-- uv__nonblock(fd, 1)
    +-- [macOS] uv__stream_try_select(tty, &fd)
    +-- uv__stream_open(tty, fd, flags)
```

### データフロー図

```
[入力]                      [処理]                              [出力]

loop (イベントループ) ----->+                                   +----> tty (初期化済みハンドル)
fd (ファイルディスクリプタ)->| uv_tty_init()                     |----> ハンドルキューに登録
                           |   guess_handle -> stream_init      |----> return 0 (成功)
                           |   -> nonblock -> stream_open       |----> return UV_E* (エラー)
                           +                                   +
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| uv.h | `include/uv.h` | ヘッダー | uv_tty_t構造体定義、uv_tty_init()宣言、uv_tty_mode_t定義 |
| tty.c (Unix) | `src/unix/tty.c` | ソース | Unix版 uv_tty_init() 実装、ヘルパー関数 |
| tty.c (Win) | `src/win/tty.c` | ソース | Windows版 uv_tty_init() 実装 |
| stream.c | `src/unix/stream.c` | ソース | uv__stream_init()、uv__stream_open() の実装 |
| core.c | `src/unix/core.c` | ソース | uv__nonblock()、uv__cloexec() 等の基本ユーティリティ |
| internal.h | `src/unix/internal.h` | ヘッダー | 内部関数宣言 |
