# 機能設計書 93-libedit

## 概要

本ドキュメントは、FreeBSDのlibeditライブラリの機能設計を記述する。libeditはコマンドライン編集およびヒストリ機能を提供するライブラリであり、GNU readlineの代替としてBSDライセンスで提供される。

### 本機能の処理概要

libeditは、対話型コマンドラインアプリケーションに行編集機能（カーソル移動、文字挿入・削除、検索等）とコマンドヒストリ機能を提供するCライブラリである。EmacsモードとViモードの両方のキーバインドをサポートし、GNU readlineとの互換レイヤも提供する。

**業務上の目的・背景**：FreeBSDの多くの対話型コマンド（sh、ftp、各種デバッガ等）がコマンドライン編集機能を必要とする。libeditはGNU readlineのGPLライセンスの制約なしに、同等の機能をBSDライセンスで提供することを目的としている。

**機能の利用シーン**：FreeBSDのシェル（/bin/sh）、FTPクライアント、デバッガ（lldb等）、SQLiteクライアント、その他の対話型ユーティリティで使用される。

**主要な処理内容**：
1. コマンドライン編集（EditLine API）：カーソル移動、挿入、削除、undo等
2. コマンドヒストリ管理（History API）：履歴の保存・検索・展開
3. トークナイズ（Tokenizer API）：入力行のトークン分割
4. ファイル名補完機能
5. キーバインドのカスタマイズ
6. 端末制御（termcap/terminfo対応）
7. ユーザ定義関数の登録

**関連システム・外部連携**：termcap/terminfoライブラリ（端末制御）、ncurses

**権限による制御**：特別な権限制御はない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 画面機能マッピングに直接の関連画面なし |

## 機能種別

ユーザインタフェース（コマンドライン編集ライブラリ）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| EditLine * | EditLine * | Yes | エディタインスタンス（el_init()で作成） | NULLチェック |
| History * | History * | Yes（ヒストリ使用時） | ヒストリインスタンス（history_init()で作成） | NULLチェック |
| FILE *in, *out, *err | FILE * | Yes | 入出力ストリーム | NULLチェック |
| const char *prog | const char * | Yes | プログラム名（el_init()の第1引数） | NULLチェック |

### 入力データソース

- 端末からのキー入力（stdin）
- .editrcファイルによるユーザ設定
- アプリケーションからのプログラム的設定（el_set()）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 編集済みテキスト行 | const char * | el_gets()の返り値。編集完了後の入力行 |
| 文字数 | int * | el_gets()で返される入力文字数 |
| LineInfo | LineInfo | buffer, cursor, lastcharの3ポインタによる行情報 |

### 出力先

- 端末画面（stdout/stderr）への編集中の表示更新
- 呼び出し元アプリケーションへの編集済みテキスト返却

## 処理フロー

### 処理シーケンス

```
1. 初期化（el_init / el_init_fd）
   └─ EditLine構造体の確保、端末設定、デフォルトキーバインド設定
2. 設定（el_set）
   └─ プロンプト関数、エディタモード、ヒストリ、キーバインド等の設定
3. 入力ループ（el_gets）
   └─ キー入力待ち → キーバインドに基づくコマンド実行 → 画面更新
4. 入力完了（改行入力時）
   └─ 編集済みテキストを呼び出し元に返却
5. 解放（el_end）
   └─ リソース解放
```

### フローチャート

```mermaid
flowchart TD
    A[el_init: 初期化] --> B[el_set: 設定]
    B --> C[el_gets: 入力待ち]
    C --> D{キー入力}
    D -->|通常文字| E[文字挿入・画面更新]
    D -->|制御キー| F[バインドされたコマンド実行]
    D -->|Enter| G[編集済みテキスト返却]
    D -->|EOF| H[NULL返却]
    E --> C
    F --> C
    G --> I[呼び出し元で処理]
    I --> C
    H --> J[el_end: 解放]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-93-1 | Emacsモードデフォルト | デフォルトのエディタモードはEmacs | el_setでEL_EDITOR未設定時 |
| BR-93-2 | CC_*返り値 | ユーザ定義関数はCC_NORM(0)からCC_REFRESH_BEEP(9)の返り値を返す | ユーザ定義関数呼び出し時 |
| BR-93-3 | .editrc設定 | ~/.editrcファイルからキーバインドや設定を読み込む | el_source()呼び出し時 |

### 計算ロジック

特に複雑な計算ロジックはない。

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

### 操作別データベース影響一覧

本機能はデータベースを使用しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| CC_ERROR (6) | コマンドエラー | エディタコマンド実行失敗 | ビープ音出力、入力継続 |
| CC_FATAL (7) | 致命的エラー | 回復不能なエラー | el_end()で解放 |
| NULL返却 | EOF/エラー | el_gets()でのEOF検出またはエラー | 入力ループ終了 |

### リトライ仕様

リトライ機構は組み込まれていない。エラー時はビープ音で通知し、入力を継続する。

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

該当なし。

## パフォーマンス要件

- キー入力に対するリアルタイム応答が必要
- ヒストリサイズはアプリケーション側で設定可能

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

- パスワード入力時のエコー抑制はアプリケーション側の責務
- .editrcファイルのパーミッション管理はユーザの責務
- ヒストリファイルにセンシティブな情報が記録される可能性がある

## 備考

- libeditはNetBSD由来のライブラリであり、FreeBSDではcontrib/libedit/以下にソースが配置されている
- バージョンはLIBEDIT_MAJOR=2, LIBEDIT_MINOR=11
- GNU readline互換レイヤ（readline.c）を提供し、readline APIでリンクするアプリケーションとの互換性を確保

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | histedit.h | `contrib/libedit/histedit.h` | 公開API宣言。EditLine型（57行目）、LineInfo構造体（62-66行目）、CC_*返り値（72-81行目）、EL_*パラメータ（135-150行目）、el_init/el_gets/el_set等のAPI宣言 |
| 1-2 | el.h | `contrib/libedit/el.h` | EditLine構造体の内部定義。端末、キーマクロ、マップ、ヒストリ等のサブ構造体への参照 |

**読解のコツ**: histedit.hが公開ヘッダ。EL_*定数はel_set/el_getの第2引数に渡すパラメータ識別子。CC_*定数はユーザ定義関数からの返り値。ワイドキャラクタ版API（el_wgets等）も存在する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | el.c | `contrib/libedit/el.c` | el_init(): EditLine構造体の初期化。el_set(): パラメータ設定のディスパッチ。el_end(): リソース解放 |

**主要処理フロー**:
1. el_init() - EditLine構造体確保、各サブシステム初期化（端末、キーマップ、ヒストリ等）
2. el_set(EL_EDITOR, "emacs") - エディタモード設定
3. el_set(EL_HIST, history, h) - ヒストリ連携設定

#### Step 3: 入力処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | read.c | `contrib/libedit/read.c` | el_gets()の実装。キー入力ループ、キーバインドルックアップ、コマンドディスパッチ |

#### Step 4: キーバインドとマッピングを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | map.c | `contrib/libedit/map.c` | Emacs/Viキーマップの定義と管理 |
| 4-2 | keymacro.c | `contrib/libedit/keymacro.c` | キーシーケンスからコマンドへのマクロマッピング |

#### Step 5: ヒストリ機能を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | history.c | `contrib/libedit/history.c` | History API実装。履歴の追加、検索、保存、読み込み |

#### Step 6: 補完・readline互換を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | filecomplete.c | `contrib/libedit/filecomplete.c` | ファイル名補完の実装 |
| 6-2 | readline.c | `contrib/libedit/readline.c` | GNU readline互換レイヤ |

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

```
el_init(prog, fin, fout, ferr)
    │
    ├─ el_set(EL_PROMPT, prompt_func)  [プロンプト設定]
    ├─ el_set(EL_EDITOR, "emacs")      [エディタモード設定]
    ├─ el_set(EL_HIST, history, h)     [ヒストリ連携]
    │
    ├─ el_gets(el, &count)             [入力取得]
    │      │
    │      ├─ read.c: キー入力ループ
    │      │      ├─ el_getc() [1文字読み取り]
    │      │      ├─ keymacro.c: キーバインドルックアップ
    │      │      ├─ map.c: コマンドディスパッチ
    │      │      └─ refresh.c: 画面更新
    │      │
    │      └─ 編集済みテキスト返却
    │
    └─ el_end(el)                      [解放]
```

### データフロー図

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

端末キー入力     ───▶ el_getc()              ───▶ 文字取得
                        │
                  keymacro/map               ───▶ コマンド特定
                        │
                  編集コマンド実行
                  (chared.c)                 ───▶ バッファ更新
                        │
                  refresh.c                  ───▶ 端末画面更新
                        │
改行入力         ───▶ el_gets() return       ───▶ const char * (編集済み行)

~/.editrc        ───▶ el_source()            ───▶ キーバインド・設定適用
ヒストリファイル  ───▶ history(H_LOAD)        ───▶ ヒストリ読み込み
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| histedit.h | `contrib/libedit/histedit.h` | ヘッダ | 公開API宣言 |
| el.h | `contrib/libedit/el.h` | ヘッダ | 内部構造体定義 |
| el.c | `contrib/libedit/el.c` | ソース | コアAPI実装（init/set/end） |
| read.c | `contrib/libedit/read.c` | ソース | 入力読み取り（el_gets） |
| map.c | `contrib/libedit/map.c` | ソース | キーマップ管理 |
| keymacro.c | `contrib/libedit/keymacro.c` | ソース | キーマクロ処理 |
| history.c | `contrib/libedit/history.c` | ソース | ヒストリAPI実装 |
| chared.c | `contrib/libedit/chared.c` | ソース | 文字編集操作 |
| refresh.c | `contrib/libedit/refresh.c` | ソース | 画面更新 |
| prompt.c | `contrib/libedit/prompt.c` | ソース | プロンプト管理 |
| emacs.c | `contrib/libedit/emacs.c` | ソース | Emacsモードコマンド |
| filecomplete.c | `contrib/libedit/filecomplete.c` | ソース | ファイル名補完 |
| readline.c | `contrib/libedit/readline.c` | ソース | GNU readline互換レイヤ |
| parse.c | `contrib/libedit/parse.c` | ソース | .editrc解析 |
| Makefile | `lib/libedit/Makefile` | ビルド | ライブラリビルド設定 |
